Forked from
Distributed Autonomous Networked Control Lab / MicroCART
1898 commits behind the upstream repository.
vrpn_FileConnection.h 13.14 KiB
#ifndef VRPN_FILE_CONNECTION_H
#define VRPN_FILE_CONNECTION_H
// {{{ vrpn_File_Connection
//
// Tom Hudson, June 1998
// This class *reads* a file written out by vrpn_Connection's logging hooks.
// The interface exactly matches that of vrpn_Connection. To do things that
// are meaningful on log replay but not on live networks, create a
// vrpn_File_Controller and pass your vrpn_File_Connection to its constructor,
// or just ask the Connection for its file connection pointer and do the
// operations directly on the FileConnection if the pointer is non-NULL.
// Logfiles are recorded as *sent*, not as translated by the receiver,
// so we still need to have all the correct names for senders and types
// registered.
// September 1998: by default preloads the entire log file on startup.
// This causes a delay (nontrivial for large logs) but should help smooth
// playback.
// }}}
#include <stdio.h> // for NULL, FILE
#include "vrpn_Configure.h" // for VRPN_API, VRPN_CALLBACK
#include "vrpn_Connection.h" // for vrpn_LOGLIST (ptr only), etc
#include "vrpn_Shared.h" // for timeval
#include "vrpn_Types.h" // for vrpn_float32, vrpn_int32, etc
struct timeval;
// Global variable used to indicate whether File Connections should
// pre-load all of their records into memory when opened. This is the
// default behavior, but fails on very large files that eat up all
// of the memory. This defaults to "true". User code should set this
// to "false" before calling vrpn_get_connection_by_name() or creating
// a new vrpn_File_Connection object if it wants that file connection
// to not preload. The value is only checked at connection creation time;
// the connection behaves consistently once created. This operation is
// useful for applications that load large data files and don't want to
// wait for them to pre-load.
extern VRPN_API bool vrpn_FILE_CONNECTIONS_SHOULD_PRELOAD;
// Global variable used to indicate whether File Connections should
// keep already-read messages stored in memory. If not, then we have
// to re-load the file starting at the beginning on rewind. This
// defaults to "true". User code should set this
// to "false" before calling vrpn_get_connection_by_name() or creating
// a new vrpn_File_Connection object if it wants that file connection
// to not preload. The value is only checked at connection creation time;
// the connection behaves consistently once created. This operation is
// useful for applications that read through large data files and
// don't have enough memory to keep them in memory at once, or for applications
// that read through only once and have no need to go back and check.
extern VRPN_API bool vrpn_FILE_CONNECTIONS_SHOULD_ACCUMULATE;
// Global variable used to indicate whether File Connections should
// play through all system messages and get to the first user message
// when opened or reset to the beginning. This defaults to "true".
// User code should set this
// to "false" before calling vrpn_get_connection_by_name() or creating
// a new vrpn_File_Connection object if it wants that file connection
// to not preload. The value is only checked at connection creation time;
// the connection behaves consistently once created. Leaving this true
// can help with offsets in time that happen at the beginning of files.
extern VRPN_API bool vrpn_FILE_CONNECTIONS_SHOULD_SKIP_TO_USER_MESSAGES;
class VRPN_API vrpn_File_Connection : public vrpn_Connection {
public:
vrpn_File_Connection(const char *station_name,
const char *local_in_logfile_name = NULL,
const char *local_out_logfile_name = NULL);
virtual ~vrpn_File_Connection(void);
virtual int mainloop(const timeval *timeout = NULL);
// returns the elapsed time in the file
virtual int time_since_connection_open(timeval *elapsed_time);
// returns the current time in the file since the epoch (UTC time).
virtual timeval get_time() { return d_time; }
virtual vrpn_File_Connection *get_File_Connection(void);
// Pretend to send pending report, really just clear the buffer.
virtual int send_pending_reports(void);
// {{{ fileconnections-specific methods (playback control)
public:
// XXX the following should not be public if we want vrpn_File_Connection
// to have the same interface as vrpn_Connection
//
// If so handler functions for messages for these operations
// should be made, and functions added to vrpn_File_Controller which
// generate the messages. This seemed like it would be messy
// since most of these functions have return values
// rate of 0.0 is paused, 1.0 is normal speed
void set_replay_rate(vrpn_float32 rate)
{
d_filetime_accum.set_replay_rate(rate);
}
vrpn_float32 get_replay_rate() { return d_filetime_accum.replay_rate(); }
// resets to the beginning of the file
// returns 0 on success
int reset(void);
// returns 1 if we're at the end of file
int eof();
// end_time for play_to_time() is an elapsed time
// returns -1 on error or EOF, 0 on success
int play_to_time(vrpn_float64 end_time);
int play_to_time(timeval end_time);
// end_filetime is an absolute time, corresponding to the
// timestamps of the entries in the file,
// returns -1 on error or EOF, 0 on success
int play_to_filetime(const timeval end_filetime);
// plays the next entry, returns -1 or error or EOF, 0 otherwise
int playone();
// plays at most one entry, but won't play past end_filetime
// returns 0 on success, 1 if at end_filetime, -1 on error or EOF
int playone_to_filetime(timeval end_filetime);
// returns the elapsed time of the file
timeval get_length();
double get_length_secs();
// returns the timestamp of the earliest in time user message
timeval get_lowest_user_timestamp();
// returns the timestamp of the greatest-in-time user message
timeval get_highest_user_timestamp();
// returns the name of the file
const char *get_filename();
// jump_to_time sets the current position to the given elapsed time
// return 1 if we got to the specified time and 0 if we didn't
int jump_to_time(vrpn_float64 newtime);
int jump_to_time(timeval newtime);
// jump_to_filetime sets the current position to the given absolute time
// return 1 if we got to the specified time and 0 if we didn't
int jump_to_filetime(timeval absolute_time);
// Limits the number of messages played out on any one call to mainloop.
// 0 => no limit.
// Used to stop continuous callback-handling when messages arrive
// at a very high rate (such as from a vrpn_Imager) or to make sure
// that we are able to pause after each frame in frame-by-frame
// playback for tracking analysis programs.
void limit_messages_played_back(vrpn_uint32 max_playback)
{
Jane_stop_this_crazy_thing(max_playback);
};
// }}}
// {{{ tokens for VRPN control messages (data members)
protected:
vrpn_int32 d_controllerId;
vrpn_int32 d_set_replay_rate_type;
vrpn_int32 d_reset_type;
vrpn_int32 d_play_to_time_type;
// long d_jump_to_time_type;
// }}}
// {{{ time-keeping
protected:
timeval d_last_told; // Last time we printed error about no open file.
timeval d_time; // current time in file
timeval d_start_time; // time of first record in file
timeval d_earliest_user_time; // time of first user message
vrpn_bool d_earliest_user_time_valid;
timeval d_highest_user_time; // time of last user message
vrpn_bool d_highest_user_time_valid;
// finds the timestamps of the earliest and highest-time user messages
void find_superlative_user_times();
// these are to be used internally when jumping around in the
// stream (e.g., for finding the earliest and latest timed
// user messages). They assume
// 1) that only functions such as advance_currentLogEntry,
// read_entry and manual traversal of d_logHead/d_logTail
// will be used.
// the functions return false if they don't save or restore the bookmark
class VRPN_API vrpn_FileBookmark {
public:
vrpn_FileBookmark();
~vrpn_FileBookmark();
bool valid;
timeval oldTime;
long int file_pos; // ftell result
vrpn_LOGLIST *oldCurrentLogEntryPtr; // just a pointer, useful for accum
// or preload
vrpn_LOGLIST *oldCurrentLogEntryCopy; // a deep copy, useful for
// no-accum, no-preload
};
bool store_stream_bookmark();
bool return_to_bookmark();
vrpn_FileBookmark d_bookmark;
// wallclock time at the (beginning of the) last call
// to mainloop that played back an event
timeval d_last_time; // XXX remove
class VRPN_API FileTime_Accumulator {
// accumulates the amount of time that we will advance
// filetime by when we next play back messages.
timeval d_filetime_accum_since_last_playback;
// wallclock time when d_filetime_accum_since_last_playback
// was last updated
timeval d_time_of_last_accum;
// scale factor between stream time and wallclock time
vrpn_float32 d_replay_rate;
public:
FileTime_Accumulator();
// return accumulated time since last reset
const timeval &accumulated(void)
{
return d_filetime_accum_since_last_playback;
}
// return last time accumulate_to was called
const timeval &time_of_last_accum(void) { return d_time_of_last_accum; }
vrpn_float32 replay_rate(void) { return d_replay_rate; }
// add (d_replay_rate * (now_time - d_time_of_last_accum))
// to d_filetime_accum_since_last_playback
// then set d_time_of_last_accum to now_time
void accumulate_to(const timeval &now_time);
// if current rate is non-zero, then time is accumulated
// before d_replay_rate is set to new_rate
void set_replay_rate(vrpn_float32 new_rate);
// set d_time_of_last_accum to now_time
// and set d_filetime_accum_since_last_playback to zero
void reset_at_time(const timeval &now_time);
};
FileTime_Accumulator d_filetime_accum;
// }}}
// {{{ actual mechanics of the logfile
protected:
char *d_fileName;
FILE *d_file;
void play_to_user_message();
// helper function for mainloop()
int need_to_play(timeval filetime);
// checks the cookie at
// the head of the log file;
// exit on error!
virtual int read_cookie(void);
virtual int read_entry(void); // appends entry to d_logTail
// returns 0 on success, 1 on EOF, -1 on error
// Steps the currentLogEntry pointer forward one.
// It handles both cases of preload and non-preload.
// returns 0 on success, 1 on EOF, -1 on error
virtual int advance_currentLogEntry(void);
virtual int close_file(void);
// }}}
// {{{ handlers for VRPN control messages that might come from
// a File Controller object that wants to control this
// File Connection.
protected:
static int VRPN_CALLBACK handle_set_replay_rate(void *, vrpn_HANDLERPARAM);
static int VRPN_CALLBACK handle_reset(void *, vrpn_HANDLERPARAM);
static int VRPN_CALLBACK handle_play_to_time(void *, vrpn_HANDLERPARAM);
// }}}
// {{{ Maintains a doubly-linked list structure that keeps
// copies of the messages from the file in memory. If
// d_accumulate is false, then there is only ever one entry
// in memory (d_currentLogEntry == d_logHead == d_logTail).
// If d_preload is true, then all of the records from the file
// are read into the list in the constructor and we merely step
// through memory when playing the streamfile. If d_preload is
// false and d_accumulate is true, then we have all of the
// records up the d_currentLogEntry in memory (d_logTail points
// to d_currentLogEntry but not to the last entry in the file
// until we get to the end of the file).
// The d_currentLogEntry should always be non-NULL unless we are
// past the end of all messages... we will either have preloaded
// all of them or else the read routine will attempt to load the
// next message each time one is played. The constructor fills it
// in with the first message, which makes it non-NULL initially.
// HOWEVER, if there are no user messages and we're asked to skip
// to the first user message then it can be NULL right after the
// constructor is called.
protected:
vrpn_LOGLIST *d_logHead; // the first read-in record
vrpn_LOGLIST *d_logTail; // the last read-in record
vrpn_LOGLIST *d_currentLogEntry; // Message that we've just loaded, or are
// at right now
vrpn_LOGLIST *d_startEntry; // potentially after initial system messages
bool d_preload; // Should THIS File Connection pre-load?
bool d_accumulate; // Should THIS File Connection accumulate?
// }}}
};
#endif // VRPN_FILE_CONNECTION_H