vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
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 try { d_imager_server_name = new char[strlen(imager_server_name) + 1]; }
16 catch (...) {
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.
26 td.pvUD = this;
28 catch (...) {
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 }
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.
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{
91 if (d_logging_thread) {
93 delete d_logging_thread;
94 d_logging_thread = NULL;
95 }
97 try {
98 delete[] d_imager_server_name;
99 } catch (...) {
100 fprintf(stderr, "vrpn_Imager_Stream_Buffer::~vrpn_Imager_Stream_Buffer(): delete failed\n");
101 return;
102 }
104 }
105}
106
108{
109 // Required from all servers
111
112 // See if we have a new image description from a logging thread. If so,
113 // fill in our values and send a description to any attached clients.
114 const char *channelBuffer = NULL;
116 d_nChannels, &channelBuffer)) {
117 int i;
118 const char *bufptr = channelBuffer;
119 for (i = 0; i < d_nChannels; i++) {
120 d_channels[i].unbuffer(&bufptr);
121 }
122 try {
123 delete[] const_cast<char *>(channelBuffer);
124 } catch (...) {
125 fprintf(stderr, "vrpn_Imager_Stream_Buffer::mainloop(): delete failed\n");
126 return;
127 }
129 }
130
131 // See if we have any messages waiting in the queue from the logging thread.
132 // If we do, get an initial count and then send that many messages to the
133 // client. Don't go looking again this iteration or we may never return --
134 // the server is quite possibly packing frames faster than we can send them.
135 // Note that the messages in the queue have already been transcoded for our
136 // and sender ID.
138 if (count) {
139 unsigned i;
140 for (i = 0; i < count; i++) {
141 // Read the next message from the queue.
144 fprintf(stderr, "vrpn_Imager_Stream_Buffer::mainloop(): Could "
145 "not retrieve message from queue\n");
146 break;
147 }
148
149 // Decrement the in-buffer frame count whenever we see a begin_frame
150 // message. This will un-block the way for later frames to be
151 // buffered.
152 if (p.type == d_begin_frame_m_id) {
154 }
155
156 // Pack and send the message to the client, then delete the buffer
157 // associated with the message. Send them all reliably. Send them
158 // all using our sender ID.
162 fprintf(stderr, "vrpn_Imager_Stream_Buffer::mainloop(): Could "
163 "not pack message\n");
164 break;
165 }
166 try {
167 delete[] const_cast<char *>(p.buffer);
168 } catch (...) {
169 fprintf(stderr, "vrpn_Imager_Stream_Buffer::mainloop(): delete failed\n");
170 return;
171 }
172 }
173 }
174}
175
176/* static */
177// This method just passes the call on to the virtual function.
186
187// Handle got first connection request by (having the second thread) create
188// a connection to the server and waiting until we get a description message
189// from the imager server we're listening to. Timeout after a while if the
190// connection cannot be made or the server does not respond.
191
193{
194 // There should be no thread in existence when this call is made.
195 // If there is, kill it and complain.
196 if (d_logging_thread->running()) {
197 struct timeval now;
198 vrpn_gettimeofday(&now, NULL);
200 "handle_got_first_connection: Thread running when it should not be",
201 now, vrpn_TEXT_ERROR);
203 return;
204 }
205
206 // Reset the shared state before starting the thread running.
208
209 // Create a thread whose userdata points at the object that
210 // created it. Then call the start function on that thread and wait
211 // for its vrpn_Imager_Remote to receive the info from the remote server
212 // it has connected to. We time out after a few seconds if we don't
213 // get the response, leaving us with a presumably broken connection
214 // to the server.
215 if (!d_logging_thread->go()) {
216 struct timeval now;
217 vrpn_gettimeofday(&now, NULL);
219 "handle_got_first_connection: Failed to start logging thread", now,
221 try {
222 delete d_logging_thread;
223 } catch (...) {
224 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_got_first_connection(): delete failed\n");
225 d_logging_thread = NULL;
226 return;
227 }
228 d_logging_thread = NULL;
229 return;
230 }
231 struct timeval start, now;
232 vrpn_gettimeofday(&start, NULL);
233 do {
234 const char *channelBuffer = NULL;
236 d_nRows, d_nCols, d_nDepth, d_nChannels, &channelBuffer)) {
237 int i;
238 const char *bufptr = channelBuffer;
239 for (i = 0; i < d_nChannels; i++) {
240 d_channels[i].unbuffer(&bufptr);
241 }
242 try {
243 delete[] const_cast<char *>(channelBuffer);
244 } catch (...) {
245 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_got_first_connection(): delete failed\n");
246 return;
247 }
248 return;
249 }
250
252 vrpn_gettimeofday(&now, NULL);
253 } while (vrpn_TimevalDiff(now, start).tv_sec < 3);
254
255 // Timed out, so we won't be hearing from the server!
256 vrpn_gettimeofday(&now, NULL);
257 send_text_message("handle_got_first_connection: Didn't hear from server.",
258 now, vrpn_TEXT_WARNING);
259}
260
261// Handle dropped last connection on our primary connection by shutting down the
262// connection to the imager server (killing the logging thread).
264{
265 if (!stop_logging_thread()) {
266 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_dropped_last_"
267 "connection(): Had to kill logging thread\n");
268 }
269}
270
271// Handles a throttle request by passing it on down to the non-blocking
272// thread to deal with.
274 void *userdata, vrpn_HANDLERPARAM p)
275{
276 const char *bufptr = p.buffer;
278 static_cast<vrpn_Imager_Stream_Buffer *>(userdata);
279
280 // Get the requested number of frames from the buffer
281 vrpn_int32 frames_to_send;
282 if (vrpn_unbuffer(&bufptr, &frames_to_send)) {
283 return -1;
284 }
285
286 me->d_shared_state.set_throttle_request(frames_to_send);
287 return 0;
288}
289
290// The function that is called to become the logging thread. It is passed
291// a pointer to "this" so that it can acces the object that created it.
292// The static function basically just unpacks the "this" pointer and
293// then calls the non-static function.
301
302// Note that it must use semaphores to get at the data that will be shared
303// between the main thread and itself.
304// This function does all the work of the logging thread, including all
305// interactions with the vrpn_Imager_Server connection(s) and the client
306// object; it forwards information both ways to the main thread that is
307// communicating with the end-user client connection.
308// DO NOT CALL VRPN message sends on the client object's connection from
309// this function or those it calls, because we're not the thread that is
310// connected to the client object's connection and such calls are not
311// thread-safe.
312// Instead, pass the data needed to make the calls to the initial thread.
314{
315 // Initialize everything to a clean state.
316 d_log_connection = NULL;
317 d_imager_remote = NULL;
318 d_server_dropped_due_to_throttle = 0; // None dropped yet!
319 d_server_frames_to_send = -1; // Send as many as you get
320
321 // Open a connection to the server object, not asking it to log anything.
322 // (Logging will be started later if we receive a message from our client.)
323 // Create a vrpn_Imager_Remote object and set its callbacks to fill things
324 // into the shared data structures.
326 if (d_log_connection == NULL) {
327 fprintf(stderr, "vrpn_Imager_Stream_Buffer::logging_thread_func(): "
328 "Cannot open connection\n");
329 return;
330 }
332 fprintf(stderr, "vrpn_Imager_Stream_Buffer::logging_thread_func(): "
333 "Cannot set up handlers\n");
334 return;
335 }
336
337 // Keep doing mainloop() on the client object(s) and checking
338 // for commands that we're supposed to issue until we're
339 // told that we're supposed to die. Sleep a little between iterations
340 // to avoid eating CPU time.
341 while (!d_shared_state.time_to_exit()) {
342 // Check to see if the client has sent a new throttling message.
343 // If so, then adjust our state accordingly. Note that we are
344 // duplicating
345 // the effort of the vrpn_Imager_Server base class here; it will still
346 // be
347 // keeping its own shadow copy of these values, but will not be doing
348 // anything
349 // with them because we'll never be calling its send routines. This
350 // duplicates
351 // the code in the handle_throttle_message() in the vrpn_Imager_Server
352 // base
353 // class.
354 vrpn_int32 frames_to_send;
355 if (d_shared_state.get_throttle_request(&frames_to_send)) {
356 // If the requested number of frames is negative, then we set
357 // for unbounded sending. The next time a begin_frame message
358 // arrives, it will start the process going again.
359 if (frames_to_send < 0) {
361
362 // If we were sending continuously, store the number of frames
363 // to send. Decrement by the number of frames already in the
364 // outgoing buffer, but don't let the number go below zero.
365 }
366 else if (d_server_frames_to_send == -1) {
367 int frames_in_queue = d_shared_state.get_frames_in_queue();
368 if (frames_to_send >= frames_in_queue) {
369 frames_to_send -= frames_in_queue;
370 }
371 d_server_frames_to_send = frames_to_send;
372
373 // If we already had a throttle limit set, then increment it
374 // by the count.
375 }
376 else {
377 d_server_frames_to_send += frames_to_send;
378 }
379 }
380
381 // Check to see if we've been asked to create new log files. If we
382 // have,
383 // then attempt to do so. If that works, pass back the names of the
384 // files
385 // created to the initial thread. If it did not work, return empty
386 // log-file
387 // names.
388 char *lil, *lol, *ril, *rol;
389 if (d_shared_state.get_logfile_request(&lil, &lol, &ril, &rol)) {
390 if (make_new_logging_connection(lil, lol, ril, rol)) {
391 d_shared_state.set_logfile_result(lil, lol, ril, rol);
392 }
393 else {
394 d_shared_state.set_logfile_result("", "", "", "");
395 }
396 // Delete the allocated space only if there were return values.
397 try {
398 delete[] lil;
399 delete[] lol;
400 delete[] ril;
401 delete[] rol;
402 } catch (...) {
403 fprintf(stderr, "vrpn_Imager_Stream_Buffer::logging_thread_func(): delete failed\n");
404 return;
405 }
406 }
407
408 // Handle all of the messages coming from the server.
409 if (d_imager_remote) {
411 }
412 if (d_log_connection) {
415 }
416
418 }
419
420 // Now that we've been told to die, clean up everything and return.
421 if (d_imager_remote) {
422 try {
423 delete d_imager_remote;
424 } catch (...) {
425 fprintf(stderr, "vrpn_Imager_Stream_Buffer::logging_thread_func(): delete failed\n");
426 return;
427 }
428 d_imager_remote = NULL;
429 }
430 if (d_log_connection) {
432 d_log_connection = NULL;
433 }
434}
435
436/* static */
445
447 const vrpn_HANDLERPARAM &p)
448{
449 // Handle begin_frame message very specially, because it has all sorts
450 // of interactions with throttling and missed-frame reporting.
452 // This duplicates code from the send_begin_frame() method in
453 // the vrpn_Imager_Server base class that handles throttling.
454 // It further adds code to handle throttling when the queue to
455 // the initial thread is too full.
456
457 // If we are throttling frames and the frame count has gone to zero,
458 // then increment the number of frames missed and do not add this
459 // message to the queue.
460 if (d_server_frames_to_send == 0) {
462 return 0;
463 }
464
465 // If there are too many frames in the queue already,
466 // add one to the number lost due to throttling (which
467 // will prevent region and end-of-frame messages until the next
468 // begin_frame message) and break without forwarding the message.
471 return 0;
472 }
473
474 // If we missed some frames due to throttling, but are now
475 // sending frames again, report how many we lost due to
476 // throttling. This is incremented both for client-requested
477 // throttling and to queue-overflow throttling.
479 // We create a new message header and body, using the server's
480 // type IDs, and then transcode and send the message through
481 // the initial connection.
482 vrpn_HANDLERPARAM tp = p;
483 vrpn_float64
484 fbuf[vrpn_CONNECTION_TCP_BUFLEN / sizeof(vrpn_float64)];
485 char *msgbuf = (char *)fbuf;
486 int buflen = sizeof(fbuf);
488
489 if (vrpn_buffer(&msgbuf, &buflen,
491 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_"
492 "messages: Can't pack count\n");
493 return -1;
494 }
495 tp.buffer = static_cast<char *>(static_cast<void *>(fbuf));
496 tp.payload_len = sizeof(fbuf) - buflen;
497
498 if (!transcode_and_send(tp)) {
499 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_"
500 "messages: Can't send discarded frames "
501 "count\n");
502 return -1;
503 }
504
506 }
507
508 // If we are throttling, then decrement the number of frames
509 // left to send.
510 if (d_server_frames_to_send > 0) {
512 }
513
514 // No throttling going on, so add the message to the outgoing queue and
515 // also increment the count of how many outstanding frames are in the
516 // queue.
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 }
523
524 // Handle the end_frame and all of the region messages in a similar
525 // manner,
526 // dropping them if throttling is going on and passing them on if not.
527 // This duplicates code from the send_end_frame() and the region
528 // send methods in the vrpn_Imager_Server base class that handles
529 // throttling.
530 }
531 else if ((p.type == d_server_end_frame_m_id) ||
536
537 // If we are discarding frames, do not queue this message.
539 return 0;
540 }
541
542 // No throttling going on, so add this message to the outgoing queue.
543 if (!transcode_and_send(p)) {
544 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
545 " Can't transcode and send\n");
546 return -1;
547 }
548
549 // Send these messages on without modification
550 // (Currently, these are description messages and discarded-frame
551 // messages. It also includes the generic pong response and any
552 // text messages.)
553 }
554 else if ((p.type == d_server_description_m_id) ||
557 if (!transcode_and_send(p)) {
558 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
559 " Can't transcode and send\n");
560 return -1;
561 }
562
563 // Ignore these messages without passing them on.
564 }
565 else if ((p.type == d_server_ping_m_id)) {
566 return 0;
567
568 // We need to throw a warning here on unexpected types so that we get
569 // some
570 // warning if additional messages are added. This code is fragile
571 // because it
572 // relies on us knowing the types of base-level and imager messages and
573 // catching
574 // them all. This way, at least we'll know if we miss one.
575 }
576 else {
577 // We create a new message header and body, using the server's
578 // type IDs, and then transcode and send the message through
579 // the initial connection. This is a text message saying that we
580 // got a message of unknown type.
581 vrpn_HANDLERPARAM tp = p;
582 char buffer[2 * sizeof(vrpn_int32) + vrpn_MAX_TEXT_LEN];
583 char msg[vrpn_MAX_TEXT_LEN];
585 tp.buffer = buffer;
586 tp.payload_len = sizeof(buffer);
587 sprintf(msg, "Unknown message type from server: %d",
588 static_cast<int>(p.type));
590 if (!transcode_and_send(tp)) {
591 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_server_messages:"
592 " Can't transcode text message\n");
593 return -1;
594 }
595 }
596
597 return 0;
598}
599
600// Transcode the sender and type fields from the logging server connection to
601// the initial client connection and pack the resulting message into the queue
602// from the logging thread to the initial thread. The data buffer is copied;
603// this space is allocated by the logging thread and must be freed by the
604// initial thread.
605// Returns true on success and false on failure. The sender is set to the
606// d_sender_id of our server object.
608{
609 // Copy the contents of the buffer to a newly-allocated one that will be
610 // passed to the initial thread.
611 char *newbuf;
612 try { newbuf = new char[p.payload_len]; }
613 catch (...) {
614 fprintf(
615 stderr,
616 "vrpn_Imager_Stream_Buffer::transcode_and_send(): Out of memory\n");
617 d_connection = NULL;
618 return false;
619 }
620 memcpy(newbuf, p.buffer, p.payload_len);
621
622 // Copy the contents of the handlerparam to a newly-allocated one that will
623 // be passed to the initial thread. Change the sender to match ours, set
624 // the
625 // buffer pointer to the new buffer, and transcode the type.
626 vrpn_HANDLERPARAM newp = p;
627 newp.buffer = newbuf;
628 newp.sender = d_sender_id;
629 newp.type = transcode_type(p.type);
630 if (newp.type == -1) {
631 fprintf(stderr, "vrpn_Imager_Stream_Buffer::transcode_and_send(): "
632 "Unknown type (%d)\n",
633 static_cast<int>(p.type));
634 try {
635 delete[] newbuf;
636 } catch (...) {
637 fprintf(stderr, "vrpn_Imager_Stream_Buffer::transcode_and_send(): delete failed\n");
638 return false;
639 }
640 return false;
641 }
642
643 // Add the message to the queue of messages going to the initial thread.
645 fprintf(stderr, "vrpn_Imager_Stream_Buffer::transcode_and_send(): "
646 "Can't queue message\n");
647 return false;
648 }
649
650 return true;
651}
652
653// Transcode the type from the logging thread's connection type to
654// the initial thread's connection type. Return -1 if we don't
655// recognize the type.
657{
659 return d_description_m_id;
660 }
661 else if (type == d_server_begin_frame_m_id) {
662 return d_begin_frame_m_id;
663 }
664 else if (type == d_server_end_frame_m_id) {
665 return d_end_frame_m_id;
666 }
669 }
670 else if (type == d_server_regionu8_m_id) {
671 return d_regionu8_m_id;
672 }
673 else if (type == d_server_regionu12in16_m_id) {
675 }
676 else if (type == d_server_regionu16_m_id) {
677 return d_regionu16_m_id;
678 }
679 else if (type == d_server_regionf32_m_id) {
680 return d_regionf32_m_id;
681 }
682 else if (type == d_server_text_m_id) {
683 return d_text_message_id;
684 }
685 else if (type == d_server_ping_m_id) {
686 return d_ping_message_id;
687 }
688 else if (type == d_server_pong_m_id) {
689 return d_pong_message_id;
690 }
691 else {
692 return -1;
693 }
694}
695
697 const char *local_in_logfile_name, const char *local_out_logfile_name,
698 const char *remote_in_logfile_name, const char *remote_out_logfile_name)
699{
700 vrpn_Connection *ret = NULL;
701
702 // Find the relevant part of the name (skip past last '@'
703 // if there is one); also find the port number.
704 const char *cname = d_imager_server_name;
705 const char *where_at; // Part of name past last '@'
706 if ((where_at = strrchr(cname, '@')) != NULL) {
707 cname = where_at + 1; // Chop off the front of the name
708 }
709
710 // Pass "true" to force_connection so that it will open a new
711 // connection even if we already have one with that name.
713 where_at, local_in_logfile_name, local_out_logfile_name,
714 remote_in_logfile_name, remote_out_logfile_name, NULL, true);
715 if (!ret || !ret->doing_okay()) {
716 struct timeval now;
717 vrpn_gettimeofday(&now, NULL);
718 fprintf(stderr, "vrpn_Imager_Stream_Buffer::open_new_log_connection: "
719 "Could not create connection (files already exist?)");
720 if (ret) {
721 try {
722 delete ret;
723 } catch (...) {
724 fprintf(stderr, "vrpn_Imager_Stream_Buffer::open_new_log_connection(): delete failed\n");
725 return NULL;
726 }
727 return NULL;
728 }
729 }
730
731 return ret;
732}
733
736{
737 // Create a vrpn_Imager_Remote on this connection and set its callbacks so
738 // that they will do what needs doing; the callbacks point to the
739 // Imager_Stream_Buffer object, not to the imager_remote object; access it
740 // through the member variable pointer.
742 if (d_imager_remote == NULL) {
743 fprintf(stderr, "vrpn_Imager_Stream_Buffer::setup_handlers_for_logging_"
744 "connection(): Cannot create vrpn_Imager_Remote\n");
745 return false;
746 }
749
750 // Figure out the remote type IDs from the server for the messages we want
751 // to forward. This is really dangerous, because we need to make sure to
752 // explicitly list all the ones we might need. If we forget an important
753 // one (or it gets added later to either the base class or the imager class)
754 // then it won't get forwarded.
756 c->register_message_type("vrpn_Imager Description");
758 c->register_message_type("vrpn_Imager Begin_Frame");
759 d_server_end_frame_m_id = c->register_message_type("vrpn_Imager End_Frame");
761 c->register_message_type("vrpn_Imager Discarded_Frames");
762 d_server_regionu8_m_id = c->register_message_type("vrpn_Imager Regionu8");
763 d_server_regionu16_m_id = c->register_message_type("vrpn_Imager Regionu16");
765 c->register_message_type("vrpn_Imager Regionu12in16");
766 d_server_regionf32_m_id = c->register_message_type("vrpn_Imager Regionf32");
767 d_server_text_m_id = c->register_message_type("vrpn_Base text_message");
768 d_server_ping_m_id = c->register_message_type("vrpn_Base ping_message");
769 d_server_pong_m_id = c->register_message_type("vrpn_Base pong_message");
770
771 // Set up handlers for the other messages from the server so that they can
772 // be passed on up to the initial thread and on to the client as
773 // appropriate.
774 // Be sure to strip the "@" part off the device name before registering the
775 // sender
776 // so that it is the same as the one used by the d_imager_remote.
780
781 return true;
782}
783
786{
787 if (!d_imager_remote) {
788 fprintf(stderr, "vrpn_Imager_Stream_Buffer::teardown_handlers_for_"
789 "logging_connection(): No imager remote\n");
790 return false;
791 }
793 this, handle_image_description) != 0) {
794 fprintf(stderr, "vrpn_Imager_Stream_Buffer::teardown_handlers_for_"
795 "logging_connection(): Cannot unregister handler\n");
796 return false;
797 }
798
799 // Tear down handlers for the other messages from the server.
803
804 try {
805 delete d_imager_remote;
806 } catch (...) {
807 fprintf(stderr, "vrpn_Imager_Stream_Buffer::teardown_handlers_for_logging_connection(): delete failed\n");
808 return false;
809 }
810 d_imager_remote = NULL;
811 return true;
812}
813
815 const char *local_in_logfile_name, const char *local_out_logfile_name,
816 const char *remote_in_logfile_name, const char *remote_out_logfile_name)
817{
818 // Open a new connection to do logging on before deleting the old one so
819 // that we keep at least one connection open to the server at all time.
820 // This will prevent it from doing its "dropped last connection" things
821 // which will include resetting the imager server.
822 vrpn_Connection *new_log_connection = open_new_log_connection(
823 local_in_logfile_name, local_out_logfile_name, remote_in_logfile_name,
824 remote_out_logfile_name);
825 if (new_log_connection == NULL) {
826 fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
827 "connection(): Cannot open connection\n");
828 return false;
829 }
830
831 // Unhook the callbacks from the existing logging connection so that
832 // we don't end up with two callbacks for each message.
834 fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
835 "connection(): Cannot teardown connection\n");
836 return false;
837 }
838
839 // Hook the callbacks up to the new connection so that we will get reports
840 // from the server.
841 if (!setup_handlers_for_logging_connection(new_log_connection)) {
842 fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
843 "connection(): Cannot setup connection\n");
844 return false;
845 }
846
847 // Mainloop the new connection object until it becomes connected or we
848 // time out. If we time out, then put things back on the old connection
849 // and tell the thread it is time to self-destruct. The way we check
850 // for connected cannot be just that the connection's connected() method
851 // returns true (because our end can be marked connected before the other
852 // end decides it has complete the connection. Rather, we check to see
853 // that we've got a new description report from the server -- indicating
854 // that it has seen the new report. This also lets us know that the old
855 // log file will have accumulated all images up to the new report, so we
856 // can shut it off without losing any images in the switch to the new
857 // log file (there may be duplicates, but not losses).
858 struct timeval start, now;
859 vrpn_gettimeofday(&start, NULL);
860 now = start;
863 (vrpn_TimevalDiff(now, start).tv_sec < 3)) {
864 new_log_connection->mainloop(); // Enable connection set-up to occur
865 new_log_connection->save_log_so_far();
866 d_log_connection->mainloop(); // Eat up (and log) any incoming messages
868 vrpn_gettimeofday(&now, NULL);
870 };
872 fprintf(stderr, "vrpn_Imager_Stream_Buffer::make_new_logging_"
873 "connection(): Could not connect new logging "
874 "connection\n");
877 new_log_connection->removeReference();
879 return false;
880 }
881
882 // Delete the old connection object by reducing its reference count.
884
885 // Set up to use the new connection
886 d_log_connection = new_log_connection;
887 return true;
888}
889
891 const char *local_in_logfile_name, const char *local_out_logfile_name,
892 const char *remote_in_logfile_name, const char *remote_out_logfile_name)
893{
894 // Request that the logging thread start new logs.
896 local_in_logfile_name, local_out_logfile_name, remote_in_logfile_name,
897 remote_out_logfile_name);
898
899 // Wait until we hear back from the logging thread or time out;
900 // return empty if timeout and the strings we got back if not.
901 // Remember to deallocated the memory if we got a response.
902 struct timeval start, now;
903 vrpn_gettimeofday(&start, NULL);
904 do {
905 char *lil, *lol, *ril, *rol;
906 if (d_shared_state.get_logfile_result(&lil, &lol, &ril, &rol)) {
907 send_report_logging(lil, lol, ril, rol);
908 try {
909 delete[] lil;
910 delete[] lol;
911 delete[] ril;
912 delete[] rol;
913 } catch (...) {
914 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_request_logging(): delete failed\n");
915 return;
916 }
917 return;
918 }
920 vrpn_gettimeofday(&now, NULL);
921 } while (vrpn_TimevalDiff(now, start).tv_sec < 2);
922
923 // Timeout, report failure of logging by saying that there are empty log
924 // file names.
925 send_report_logging("", "", "", "");
926}
927
929{
930 char *local_in;
931 char *local_out;
932 char *remote_in;
933 char *remote_out;
934 d_shared_state.get_logfile_names(&local_in, &local_out, &remote_in,
935 &remote_out);
936 send_report_logging(local_in, local_out, remote_in, remote_out);
937 try {
938 if (local_in) delete[] local_in;
939 if (local_out) delete[] local_out;
940 if (remote_in) delete[] remote_in;
941 if (remote_out) delete[] remote_out;
942 } catch (...) {
943 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_request_logging_status(): delete failed\n");
944 return;
945 }
946}
947
948/* Static */
949// We've gotten a new imager description, so fill it into the shared data
950// structure
951// so that the parent object can hear about it.
953 void *pvISB, const struct timeval)
954{
956 static_cast<vrpn_Imager_Stream_Buffer *>(pvISB);
957
958 // Pack up description messages for all of the channels into a buffer that
959 // is at
960 // least large enough to hold them all.
961 // msgbuf must be float64-aligned!
962 vrpn_float64 *fbuf;
963 try { fbuf = new vrpn_float64[vrpn_CONNECTION_TCP_BUFLEN / sizeof(vrpn_float64)]; }
964 catch (...) {
965 fprintf(stderr, "vrpn_Imager_Stream_Buffer::handle_image_description():"
966 " Out of memory\n");
968 me->d_connection = NULL;
969 return;
970 }
971 char *buffer = reinterpret_cast<char *>(fbuf);
972 int i;
973 char *bufptr = buffer;
974 vrpn_int32 buflen = sizeof(vrpn_float64) * vrpn_CONNECTION_TCP_BUFLEN /
975 sizeof(vrpn_float64);
976 for (i = 0; i < me->d_imager_remote->nChannels(); i++) {
977 me->d_imager_remote->channel(i)->buffer(&bufptr, &buflen);
978 }
979
983 buffer);
984
985 // We've gotten a description report on the new connection, so we're ready
986 // to drop the old connection.
988}
989
990// Stop the logging thread function, cleanly if possible. Returns true if
991// the function stopped cleanly, false if it had to be killed.
993{
994 // Set the flag telling the logging thread to stop.
996
997 // Wait for up to three seconds for the logging thread to die a clean death.
998 // If it does, return true.
999 struct timeval start, now;
1000 vrpn_gettimeofday(&start, NULL);
1001 do {
1002 if (!d_logging_thread->running()) {
1003 return true;
1004 }
1005 vrpn_SleepMsecs(1);
1006 vrpn_gettimeofday(&now, NULL);
1007 } while (vrpn_TimevalDiff(now, start).tv_sec < 3);
1008
1010 return false;
1011}
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)
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.
vrpn_Connection * d_connection
Connection that this object talks to.
vrpn_int32 d_pong_message_id
Server telling that it is there.
vrpn_int32 d_sender_id
Sender ID registered with the connection.
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
vrpn_int32 d_text_message_id
ID for text messages.
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.
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 d_ping_message_id
Ask the server if they are there.
Generic connection class not specific to the transport mechanism.
virtual vrpn_int32 register_message_type(const char *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...
virtual int save_log_so_far()
Save any messages on any endpoints which have been logged so far.
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...
virtual vrpn_bool doing_okay(void) const
Returns vrpn_true if the connection is okay, vrpn_false if not.
virtual int unregister_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
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 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...
bool buffer(char **insertPt, vrpn_int32 *buflen) const
Definition vrpn_Imager.h:83
bool unbuffer(const char **buffer)
Definition vrpn_Imager.h:99
This is the class users deal with: it tells the format and the region data when it arrives.
virtual int register_description_handler(void *userdata, vrpn_IMAGERDESCRIPTIONHANDLER handler)
Register a handler for when the object's description changes (if desired).
const vrpn_Imager_Channel * channel(unsigned chanNum) const
Accessors for the member variables: can be queried in the handler for object changes.
virtual void mainloop(void)
XXX It could be nice to let the user specify separate callbacks for.
virtual int unregister_description_handler(void *userdata, vrpn_IMAGERDESCRIPTIONHANDLER handler)
bool send_description(void)
Sends a description of the imager so the remote can process the region messages.
bool teardown_handlers_for_logging_connection(vrpn_Connection *c)
vrpn_Imager_Stream_Shared_State d_shared_state
bool setup_handlers_for_logging_connection(vrpn_Connection *c)
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)
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)
static void static_logging_thread_func(vrpn_ThreadData &threadData)
virtual void mainloop(void)
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
bool transcode_and_send(const vrpn_HANDLERPARAM &p)
static int VRPN_CALLBACK static_handle_throttle_message(void *userdata, vrpn_HANDLERPARAM p)
vrpn_Imager_Stream_Buffer(const char *name, const char *imager_server_name, vrpn_Connection *c)
static int VRPN_CALLBACK static_handle_server_messages(void *pvISB, vrpn_HANDLERPARAM p)
virtual void handle_dropped_last_connection(void)
virtual void handle_got_first_connection(void)
vrpn_int32 transcode_type(vrpn_int32 type)
static int VRPN_CALLBACK static_handle_got_first_connection(void *userdata, vrpn_HANDLERPARAM p)
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)
static void VRPN_CALLBACK handle_image_description(void *pvISB, const struct timeval msg_time)
int handle_server_messages(const vrpn_HANDLERPARAM &p)
bool insert_logger_to_client_message(const vrpn_HANDLERPARAM &p)
void set_logfile_result(const char *lil, const char *lol, const char *ril, const char *rol)
void set_logfile_request(const char *lil, const char *lol, const char *ril, const char *rol)
bool get_logfile_result(char **lil, char **lol, char **ril, char **rol)
bool get_logfile_request(char **lil, char **lol, char **ril, char **rol)
bool retrieve_logger_to_client_message(vrpn_HANDLERPARAM *p)
bool get_imager_description(vrpn_int32 &nRows, vrpn_int32 &nCols, vrpn_int32 &nDepth, vrpn_int32 &nChannels, const char **channelBuffer)
void set_throttle_request(vrpn_int32 throttle_count)
bool set_imager_description(vrpn_int32 nRows, vrpn_int32 nCols, vrpn_int32 nDepth, vrpn_int32 nChannels, const char *channelBuffer)
bool get_throttle_request(vrpn_int32 *throttle_count)
void get_logfile_names(char **local_in, char **local_out, char **remote_in, char **remote_out)
vrpn_int32 d_description_m_id
vrpn_int32 nDepth(void) const
vrpn_int32 nChannels(void) const
vrpn_int32 d_regionf32_m_id
vrpn_int32 d_nCols
vrpn_int32 d_nRows
vrpn_int32 nCols(void) const
vrpn_int32 d_throttle_frames_m_id
vrpn_int32 d_begin_frame_m_id
vrpn_int32 d_regionu8_m_id
vrpn_int32 d_end_frame_m_id
vrpn_int32 d_regionu12in16_m_id
vrpn_int32 nRows(void) const
vrpn_int32 d_nChannels
vrpn_int32 d_regionu16_m_id
vrpn_int32 d_discarded_frames_m_id
vrpn_int32 d_nDepth
vrpn_Imager_Channel d_channels[vrpn_IMAGER_MAX_CHANNELS]
bool running()
This structure is what is passed to a vrpn_Connection message callback.
const char * buffer
struct timeval msg_time
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
@ vrpn_TEXT_WARNING
@ vrpn_TEXT_ERROR
const unsigned vrpn_MAX_TEXT_LEN
#define VRPN_CALLBACK
const char * vrpn_got_first_connection
These are the strings that define the system-generated message types that tell when connections are r...
char * vrpn_copy_service_name(const char *fullname)
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).
const vrpn_uint32 vrpn_CONNECTION_RELIABLE
Classes of service for messages, specify multiple by ORing them together Priority of satisfying these...
const int vrpn_ANY_SENDER
vrpn_ANY_SENDER can be used to register callbacks on a given message type from 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....
const int vrpn_CONNECTION_TCP_BUFLEN
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.
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.
timeval vrpn_TimevalDiff(const timeval &tv1, const timeval &tv2)
void vrpn_SleepMsecs(double dMilliSecs)
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99