vrpn  07.33
Virtual Reality Peripheral Network
vrpn_CerealBox.C
Go to the documentation of this file.
1 // vrpn_CerealBox.C
2 // This is a driver for the BG Systems CerealBox controller.
3 // This box is a serial-line device that allows the user to connect
4 // analog inputs, digital inputs, digital outputs, and digital
5 // encoders and read from them over RS-232. You can find out more
6 // at www.bgsystems.com. This code is written for version 3.07 of
7 // the EEPROM code.
8 // This code is based on their driver code, which was posted
9 // on their web site as of the summer of 1999. This code reads the
10 // characters as they arrive, rather than waiting "long enough" for
11 // them to get here; this should allow the CerealBox to be used at
12 // the same time as a tracking device without slowing the tracker
13 // down.
14 
15 #include <stdio.h> // for fprintf, stderr, perror, etc
16 #include <string.h> // for NULL, strlen, strncmp
17 
18 #include "vrpn_BaseClass.h" // for ::vrpn_TEXT_ERROR
19 #include "vrpn_CerealBox.h"
20 #include "vrpn_Serial.h"
21 #include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday
22 
23 #undef VERBOSE
24 
25 static const char offset = 0x21; // Offset added to some characters to avoid ctl chars
26 static const double REV_PER_TICK = 1.0/4096; // How many revolutions per encoder tick?
27 
28 // Defines the modes in which the box can find itself.
29 #define STATUS_RESETTING (-1) // Resetting the box
30 #define STATUS_SYNCING (0) // Looking for the first character of report
31 #define STATUS_READING (1) // Looking for the rest of the report
32 
33 #define MAX_TIME_INTERVAL (2000000) // max time between reports (usec)
34 
35 // This creates a vrpn_CerealBox and sets it to reset mode. It opens
36 // the serial device using the code in the vrpn_Serial_Analog constructor.
37 // The box seems to autodetect the baud rate when the "T" command is sent
38 // to it.
40  const char * port, int baud,
41  const int numbuttons, const int numchannels, const int numencoders):
42  vrpn_Serial_Analog(name, c, port, baud),
43  vrpn_Button_Filter(name, c),
44  vrpn_Dial(name, c),
45  _numbuttons(numbuttons),
46  _numchannels(numchannels),
47  _numencoders(numencoders)
48 {
49  // Verify the validity of the parameters
50  if (_numbuttons > 24) {
51  fprintf(stderr,"vrpn_CerealBox: Can only support 24 buttons, not %d\n",
52  _numbuttons);
53  _numbuttons = 24;
54  }
55  if (_numchannels > 8) {
56  fprintf(stderr,"vrpn_CerealBox: Can only support 8 analogs, not %d\n",
57  _numchannels);
58  _numchannels = 8;
59  }
60  if (_numencoders > 8) {
61  fprintf(stderr,"vrpn_CerealBox: Can only support 8 encoders, not %d\n",
62  _numencoders);
63  _numencoders = 8;
64  }
65 
66  // Set the parameters in the parent classes
70 
71  // Set the status of the buttons, analogs and encoders to 0 to start
72  clear_values();
73 
74  // Set the mode to reset
76 }
77 
79 {
80  int i;
81 
82  for (i = 0; i < _numbuttons; i++) {
84  }
85  for (i = 0; i < _numchannels; i++) {
87  }
88  for (i = 0; i < _numencoders; i++) {
89  vrpn_Dial::dials[i] = 0.0;
90  }
91 }
92 
93 // This routine will reset the CerealBox, asking it to send the requested number
94 // of analogs, buttons and encoders. It verifies that it can communicate with the
95 // device and checks the version of the EPROMs in the device is 3.07.
96 
98 {
99  struct timeval timeout;
100  unsigned char inbuf[45];
101  const char *Cpy = "Copyright (c), BG Systems";
102  int major, minor, bug; // Version of the EEPROM
103  unsigned char reset_str[32]; // Reset string sent to box
104  int ret;
105 
106  //-----------------------------------------------------------------------
107  // Set the values back to zero for all buttons, analogs and encoders
108  clear_values();
109 
110  //-----------------------------------------------------------------------
111  // Check that the box exists and has the correct EEPROM version. The
112  // "T" command to the box should cause the 44-byte EEPROM string to be
113  // returned. This string defines the version and type of the box.
114  // Give it a reasonable amount of time to finish (2 seconds), then timeout
116  vrpn_write_characters(serial_fd, (const unsigned char *)"T", 1);
117  timeout.tv_sec = 2;
118  timeout.tv_usec = 0;
119  ret = vrpn_read_available_characters(serial_fd, inbuf, 44, &timeout);
120  inbuf[44] = 0; // Make sure string is NULL-terminated
121  if (ret < 0) {
122  perror("vrpn_CerealBox: Error reading from box\n");
123  return -1;
124  }
125  if (ret == 0) {
126  fprintf(stderr,"vrpn_CerealBox: No response from box\n");
127  return -1;
128  }
129  if (ret != 44) {
130  fprintf(stderr,"vrpn_CerealBox: Got %d of 44 expected characters\n",ret);
131  return -1;
132  }
133 
134  // Parse the copyright string for the version and other information
135  // Code here is similar to check_rev() function in the BG example code.
136  if (strncmp((char *)inbuf, Cpy, strlen(Cpy))) {
137  fprintf(stderr,"vrpn_CerealBox: Copyright string mismatch: %s\n",inbuf);
138  return -1;
139  }
140  major = inbuf[38] - '0';
141  minor = inbuf[40] - '0';
142  bug = inbuf[41] - '0';
143  if ( (3 != major) || (0 != minor) || (7 != bug) ) {
144  fprintf(stderr, "vrpn_CerealBox: Bad EEPROM version (want 3.07, got %d.%d%d)\n",
145  major, minor, bug);
146  return -1;
147  }
148  printf("vrpn_CerealBox: Box of type %c found\n",inbuf[42]);
149 
150  //-----------------------------------------------------------------------
151  // Compute the proper string to initialize the device based on how many
152  // of each type of input/output that is selected. This follows init_cereal()
153  // in BG example code.
154  { int i;
155  char ana1_4 = 0; // Bits 1-4 do analog channels 1-4
156  char ana5_8 = 0; // Bits 1-4 do analog channels 5-8
157  char dig1_3 = 0; // Bits 1-3 enable groups of 8 inputs
158  char enc1_4 = 0; // Bits 1-4 enable encoders 1-4
159  char enc5_8 = 0; // Bits 1-4 enable encoders 5-8
160 
161  // Figure out which analog channels to use and set them
162  for (i = 0; i < 4; i++) {
163  if (i < _numchannels) {
164  ana1_4 |= (1<<i);
165  }
166  }
167  for (i = 0; i < 4; i++) {
168  if (i+4 < _numchannels) {
169  ana5_8 |= (1<<i);
170  }
171  }
172 
173  // Figure out which banks of digital inputs to use and set them
174  for (i = 0; i < _numbuttons; i++) {
175  dig1_3 |= (1 << (i/8));
176  }
177 
178  // Figure out which encoder channels to use and set them
179  for (i = 0; i < 4; i++) {
180  if (i < _numencoders) {
181  enc1_4 |= (1<<i);
182  }
183  }
184  for (i = 0; i < 4; i++) {
185  if (i+4 < _numencoders) {
186  enc5_8 |= (1<<i);
187  }
188  }
189 
190  reset_str[0] = 'c';
191  reset_str[1] = (unsigned char)(ana1_4 + offset); // Hope we don't need to set baud rate
192  reset_str[2] = (unsigned char)((ana5_8 | (dig1_3 << 4)) + offset);
193  reset_str[3] = (unsigned char)(0 + offset);
194  reset_str[4] = (unsigned char)(0 + offset);
195  reset_str[5] = (unsigned char)(enc1_4 + offset);
196  reset_str[6] = (unsigned char)(enc1_4 + offset); // Set encoders 1-4 for incremental
197  reset_str[7] = (unsigned char)(enc5_8 + offset);
198  reset_str[8] = (unsigned char)(enc5_8 + offset); // Set encoders 5-8 for incremental
199  reset_str[9] = '\n';
200  reset_str[10] = 0;
201  }
202 
203  // Send the string and then wait for an acknowledgement from the box.
204  vrpn_write_characters(serial_fd, reset_str, 10);
205  timeout.tv_sec = 2;
206  timeout.tv_usec = 0;
207  ret = vrpn_read_available_characters(serial_fd, inbuf, 2, &timeout);
208  if (ret < 0) {
209  perror("vrpn_CerealBox: Error reading ack from box\n");
210  return -1;
211  }
212  if (ret == 0) {
213  fprintf(stderr,"vrpn_CerealBox: No ack from box\n");
214  return -1;
215  }
216  if (ret != 2) {
217  fprintf(stderr,"vrpn_CerealBox: Got %d of 2 expected ack characters\n",ret);
218  return -1;
219  }
220  if (inbuf[0] != 'a') {
221  fprintf(stderr,"vrpn_CerealBox: Bad ack: wanted 'a', got '%c'\n",inbuf[0]);
222  return -1;
223  }
224 
225  //-----------------------------------------------------------------------
226  // Ask the box to send a report, and go into SYNCING mode to get it.
227  vrpn_write_characters(serial_fd, (unsigned char *)"pE", 2);
229  printf("CerealBox reset complete.\n");
230 
231  //-----------------------------------------------------------------------
232  // Figure out how many characters to expect in each report from the device
233  // There is a 'p' to start and '\n' to finish, two bytes for each analog
234  // value, 4 bytes for each encoder. Buttons are enabled in banks of 8,
235  // but each bank of 8 is returned in 2 bytes (4 bits each).
237  ((_numbuttons+7) / 8) * 2;
238 
239  vrpn_gettimeofday(&timestamp, NULL); // Set watchdog now
240  return 0;
241 }
242 
243 // This function will read characters until it has a full report, then
244 // put that report into the time, analog, button and encoder fields and call
245 // the report methods on these. The time stored is that of
246 // the first character received as part of the report. Reports start with
247 // the header "p" and end with "\n", and is of the length _expected_chars.
248 // If we get a report that is not valid, we assume that we have lost a
249 // character or something and re-synchronize by waiting
250 // until the start-of-report character ('p') comes around again.
251 // The routine that calls this one
252 // makes sure we get a full reading often enough (ie, it is responsible
253 // for doing the watchdog timing to make sure the box hasn't simply
254 // stopped sending characters).
255 // Returns 1 if got a complete report, 0 otherwise.
256 
258 {
259  int ret; // Return value from function call to be checked
260  int i; // Loop counter
261  int nextchar = 1; // Index of the next character to read
262 
263  //--------------------------------------------------------------------
264  // The reports are each _expected_chars characters long, and each start with an
265  // ASCII 'p' character. If we're synching, read a byte at a time until we
266  // find a 'p' character.
267  //--------------------------------------------------------------------
268 
269  if (status == STATUS_SYNCING) {
270  // Try to get a character. If none, just return.
272  return 0;
273  }
274 
275  // If it is not a 'p', we don't want it but we
276  // need to look at the next one, so just return and stay
277  // in Syncing mode so that we will try again next time through.
278  if ( _buffer[0] != 'p') {
279  fprintf(stderr,"vrpn_CerealBox: Syncing (looking for 'p', "
280  "got '%c')\n", _buffer[0]);
281  return 0;
282  }
283 
284  // Got the first character of a report -- go into READING mode
285  // and record that we got one character at this time. The next
286  // bit of code will attempt to read the rest of the report.
287  // The time stored here is as close as possible to when the
288  // report was generated.
289  _bufcount = 1;
292 #ifdef VERBOSE
293  printf("... Got the 1st char\n");
294 #endif
295  }
296 
297  //--------------------------------------------------------------------
298  // Read as many bytes of this report as we can, storing them
299  // in the buffer. We keep track of how many have been read so far
300  // and only try to read the rest. The routine that calls this one
301  // makes sure we get a full reading often enough (ie, it is responsible
302  // for doing the watchdog timing to make sure the device hasn't simply
303  // stopped sending characters).
304  //--------------------------------------------------------------------
305 
308  if (ret == -1) {
309  send_text_message("vrpn_CerealBox: Error reading", timestamp, vrpn_TEXT_ERROR);
311  return 0;
312  }
313  _bufcount += ret;
314 #ifdef VERBOSE
315  if (ret != 0) printf("... got %d characters (%d total)\n",ret, _bufcount);
316 #endif
317  if (_bufcount < _expected_chars) { // Not done -- go back for more
318  return 0;
319  }
320 
321  //--------------------------------------------------------------------
322  // We now have enough characters to make a full report. Check to make
323  // sure that its format matches what we expect. If it does, the next
324  // section will parse it. If it does not, we need to go back into
325  // synch mode and ignore this report. A well-formed report has the
326  // first character 'p', and the last character is '\n'.
327  //--------------------------------------------------------------------
328 
329  if (_buffer[0] != 'p') {
331  fprintf(stderr,"vrpn_CerealBox: Not 'p' in record\n");
332  return 0;
333  }
334  if (_buffer[_expected_chars-1] != '\n') {
336  fprintf(stderr,"vrpn_CerealBox: No carriage return in record\n");
337  return 0;
338  }
339 
340 #ifdef VERBOSE
341  printf("got a complete report (%d of %d)!\n", _bufcount, _expected_chars);
342 #endif
343 
344  //--------------------------------------------------------------------
345  // Ask the device to send us another report. Ideally, this would be
346  // sent earlier so that we can overlap with the previous report. However,
347  // when we ask after the first character we start losing parts of the
348  // reports when we've turned on a lot of inputs. So, we do it here
349  // after the report has come in.
350  //--------------------------------------------------------------------
351 
352  vrpn_write_characters(serial_fd, (unsigned char *)"pE", 2);
353 
354  //--------------------------------------------------------------------
355  // Decode the report and store the values in it into the parent classes
356  // (analog, button and encoder). This code is modelled on the routines
357  // convert_serial() and unpack_encoders() in the BG systems code.
358  //--------------------------------------------------------------------
359 
360  { // Digital code. There appear to be 4 bits (four buttons) stored
361  // in each byte, in the low-order 4 bits after the offset of 0x21
362  // has been removed from each byte. They seem to come in highest-
363  // buttons first, with the highest within each bank in the leftmost
364  // bit. This assumes we are not using MP for digital inputs.
365 
366  int i;
367  int numbuttonchars;
368 
369  // Read two characters for each eight buttons that are on, from the
370  // highest bank down.
371  numbuttonchars = ((_numbuttons+7) / 8) * 2;
372  for (i = numbuttonchars-1; i >= 0; i--) {
373  // Find the four bits for these buttons by subtracting
374  // the offset to get them into the low-order 4 bits
375  char bits = (char)(_buffer[nextchar++] - offset);
376 
377  // Set the buttons for each bit
378  buttons[ i*4 + 3 ] = ( (bits & 0x08) != 0);
379  buttons[ i*4 + 2 ] = ( (bits & 0x04) != 0);
380  buttons[ i*4 + 1 ] = ( (bits & 0x02) != 0);
381  buttons[ i*4 + 0 ] = ( (bits & 0x01) != 0);
382  }
383  }
384 
385  {// Analog code. Looks like there are two characters for each
386  // analog value; this conversion code grabbed right from the
387  // BG code. They seem to come in lowest-numbered first.
388 
389  int intval, i;
390  double realval;
391  for (i = 0; i < _numchannels; i++) {
392  intval = (0x3f & (_buffer[nextchar++]-offset)) << 6;
393  intval |= (0x3f & (_buffer[nextchar++]-offset));
394  realval = -1.0 + (2.0 * intval/4095.0);
395  channel[i] = realval;
396  }
397  }
398 
399  { // Encoders. They come packed as 24-bit values with 6 bits in
400  // each byte (offset by 0x21). They seem to come least-significant
401  // part first. This decoding is valid only for incremental
402  // encoders. Remember to convert the encoder values into fractions
403  // of a revolution.
404 
405  for (i = 0; i < _numencoders; i++) {
406  int enc0, enc1, enc2, enc3;
407  long increment;
408 
409  enc0 = (_buffer[nextchar++]-offset) & 0x3f;
410  increment = enc0;
411  enc1 = (_buffer[nextchar++]-offset) & 0x3f;
412  increment |= enc1 << 6;
413  enc2 = (_buffer[nextchar++]-offset) & 0x3f;
414  increment |= enc2 << 12;
415  enc3 = (_buffer[nextchar++]-offset) & 0x3f;
416  increment |= enc3 << 18;
417  if ( increment & 0x800000 ) {
418  dials[i] = (int)(increment - 16777216) * REV_PER_TICK;
419  } else {
420  dials[i] = (int)(increment) * REV_PER_TICK;
421  }
422  }
423  }
424 
425  //--------------------------------------------------------------------
426  // Done with the decoding, send the reports and go back to syncing
427  //--------------------------------------------------------------------
428 
429  report_changes();
431  _bufcount = 0;
432  return 1;
433 }
434 
435 void vrpn_CerealBox::report_changes(vrpn_uint32 class_of_service)
436 {
440 
441  vrpn_Analog::report_changes(class_of_service);
444 }
445 
446 void vrpn_CerealBox::report(vrpn_uint32 class_of_service)
447 {
451 
452  vrpn_Analog::report(class_of_service);
455 }
456 
457 // This routine is called each time through the server's main loop. It will
458 // take a course of action depending on the current status of the cerealbox,
459 // either trying to reset it or trying to get a reading from it.
461 {
462  // Call the generic server mainloop, since we are a server
463  server_mainloop();
464 
465  switch(status) {
466  case STATUS_RESETTING:
467  reset();
468  break;
469 
470  case STATUS_SYNCING:
471  case STATUS_READING:
472  {
473  // It turns out to be important to get the report before checking
474  // to see if it has been too long since the last report. This is
475  // because there is the possibility that some other device running
476  // in the same server may have taken a long time on its last pass
477  // through mainloop(). Trackers that are resetting do this. When
478  // this happens, you can get an infinite loop -- where one tracker
479  // resets and causes the other to timeout, and then it returns the
480  // favor. By checking for the report here, we reset the timestamp
481  // if there is a report ready (ie, if THIS device is still operating).
482  while (get_report()) {}; // Keep getting reports as long as they come
483  struct timeval current_time;
484  vrpn_gettimeofday(&current_time, NULL);
485  if ( vrpn_TimevalDuration(current_time,timestamp) > MAX_TIME_INTERVAL) {
486  fprintf(stderr,"CerealBox failed to read... current_time=%ld:%ld, timestamp=%ld:%ld\n",
487  current_time.tv_sec, static_cast<long>(current_time.tv_usec),
488  timestamp.tv_sec, static_cast<long>(timestamp.tv_usec));
489  send_text_message("Too long since last report, resetting", current_time, vrpn_TEXT_ERROR);
491  }
492  }
493  break;
494 
495  default:
496  fprintf(stderr,"vrpn_CerealBox: Unknown mode (internal error)\n");
497  break;
498  }
499 }
vrpn_Serial_Analog
Definition: vrpn_Analog.h:63
vrpn_Dial::report
virtual void report(void)
Definition: vrpn_Dial.C:82
vrpn_Button::report_changes
virtual void report_changes(void)
Definition: vrpn_Button.C:422
vrpn_Analog::status
int status
Definition: vrpn_Analog.h:43
vrpn_BaseClass.h
vrpn_TimevalDuration
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
Definition: vrpn_Shared.C:129
vrpn_Button_Filter::report_changes
virtual void report_changes(void)
Definition: vrpn_Button.C:382
STATUS_READING
#define STATUS_READING
Definition: vrpn_CerealBox.C:31
vrpn_CerealBox::_numchannels
int _numchannels
Definition: vrpn_CerealBox.h:30
vrpn_Analog::channel
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
vrpn_CerealBox.h
STATUS_SYNCING
#define STATUS_SYNCING
Definition: vrpn_CerealBox.C:30
vrpn_Analog::report
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report whether something has changed or not (for servers) Optionally, tell what time to stamp ...
Definition: vrpn_Analog.C:94
vrpn_CerealBox::get_report
virtual int get_report(void)
Definition: vrpn_CerealBox.C:257
vrpn_Analog::timestamp
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_CerealBox::_expected_chars
unsigned _expected_chars
Definition: vrpn_CerealBox.h:33
vrpn_CerealBox::_numbuttons
int _numbuttons
Definition: vrpn_CerealBox.h:29
vrpn_Serial.h
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
vrpn_Button::num_buttons
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
vrpn_CerealBox::_buffer
unsigned char _buffer[512]
Definition: vrpn_CerealBox.h:34
vrpn_Button::buttons
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:44
vrpn_CerealBox::mainloop
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
Definition: vrpn_CerealBox.C:460
vrpn_TEXT_ERROR
Definition: vrpn_BaseClass.h:103
vrpn_flush_input_buffer
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
Definition: vrpn_Serial.C:435
vrpn_CerealBox::_numencoders
int _numencoders
Definition: vrpn_CerealBox.h:31
vrpn_Dial
Definition: vrpn_Dial.h:21
vrpn_CerealBox::reset
virtual int reset(void)
Definition: vrpn_CerealBox.C:97
vrpn_Shared.h
vrpn_Button::timestamp
struct timeval timestamp
Definition: vrpn_Button.h:48
vrpn_Dial::dials
vrpn_float64 dials[vrpn_DIAL_MAX]
Definition: vrpn_Dial.h:26
vrpn_Dial::report_changes
virtual void report_changes(void)
Definition: vrpn_Dial.C:54
vrpn_CerealBox::clear_values
virtual void clear_values(void)
Definition: vrpn_CerealBox.C:78
vrpn_Serial_Analog::serial_fd
int serial_fd
Definition: vrpn_Analog.h:72
vrpn_Connection
Generic connection class not specific to the transport mechanism.
Definition: vrpn_Connection.h:510
vrpn_Analog::num_channel
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
vrpn_gettimeofday
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
STATUS_RESETTING
#define STATUS_RESETTING
Definition: vrpn_CerealBox.C:29
vrpn_Button::lastbuttons
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
vrpn_CerealBox::vrpn_CerealBox
vrpn_CerealBox(const char *name, vrpn_Connection *c, const char *port, int baud, const int numbuttons, const int numchannels, const int numencoders)
Definition: vrpn_CerealBox.C:39
vrpn_read_available_characters
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
Definition: vrpn_Serial.C:512
vrpn_CerealBox::_bufcount
unsigned _bufcount
Definition: vrpn_CerealBox.h:35
vrpn_CerealBox::_status
int _status
Definition: vrpn_CerealBox.h:28
vrpn_Analog::last
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39
MAX_TIME_INTERVAL
#define MAX_TIME_INTERVAL
Definition: vrpn_CerealBox.C:33
vrpn_write_characters
int vrpn_write_characters(int comm, const unsigned char *buffer, size_t bytes)
Write the buffer to the serial port.
Definition: vrpn_Serial.C:643
vrpn_BaseClassUnique::send_text_message
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.
Definition: vrpn_BaseClass.C:568
vrpn_Analog::report_changes
virtual void report_changes(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report only if something has changed (for servers) Optionally, tell what time to stamp the val...
Definition: vrpn_Analog.C:71
vrpn_Dial::num_dials
vrpn_int32 num_dials
Definition: vrpn_Dial.h:27
vrpn_Dial::timestamp
struct timeval timestamp
Definition: vrpn_Dial.h:28
vrpn_BaseClassUnique::server_mainloop
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
Definition: vrpn_BaseClass.C:603
vrpn_CerealBox::timestamp
struct timeval timestamp
Definition: vrpn_CerealBox.h:37
vrpn_Button_Filter
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:65