/
vrpn_FileConnection.h
326 lines (264 loc) · 13.1 KB
/
vrpn_FileConnection.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#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