vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_Tng3.C
Go to the documentation of this file.
1// vrpn_Tng3.C
2// This is a driver for an TNG 3 controller.
3// This box is a serial-line device that allows the user to connect
4// 8 analog and 8 digital inputs and, read from them over RS-232.
5//
6// This code is based on the Tng3 code from pulsar.org
7
8#include <math.h> // for floor
9#include <stdio.h> // for fprintf, stderr, printf
10
11#include "vrpn_BaseClass.h" // for ::vrpn_TEXT_WARNING, etc
12#include "vrpn_Serial.h"
13#include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday
14#include "vrpn_Tng3.h"
15
16#define VRPN_TIMESTAMP_MEMBER _timestamp // Configuration required for vrpn_MessageMacros in this class.
17#include "vrpn_MessageMacros.h" // for VRPN_MSG_INFO, VRPN_MSG_WARNING, VRPN_MSG_ERROR
18
19#undef VERBOSE
20
21#define MAX_TCHANNELS 8
22#define MAX_TBUTTONS 8
23
24#define PAUSE_RESET .015
25#define PAUSE_END .015
26#define PAUSE_RESTORE 2.0
27#define PAUSE_BYTE .015
28
29#define DATA_RECORD_LENGTH 9 // 9 bytes follow the start byte
30
31
32// Defines the modes in which the box can find itself.
33#define STATUS_RESETTING (-1) // Resetting the box
34#define STATUS_SYNCING (0) // Looking for the first character of report
35#define STATUS_READING (1) // Looking for the rest of the report
36#define MAX_TIME_INTERVAL (2000000) // max time between reports (usec)
37
38
39static void pause (double delay) {
40 if (delay < 0)
41 delay = 0;
42 unsigned long interval = (long) floor(1000000.0 * delay);
43
44 struct timeval start, now;
45 vrpn_gettimeofday (&start, NULL);
46
47 do {
48 vrpn_gettimeofday (&now, NULL);
49 } while (vrpn_TimevalDuration(now, start) < interval);
50
51}
52
53// This creates a vrpn_Tng3 and sets it to reset mode. It opens
54// the serial device using the code in the vrpn_Serial_Analog constructor.
55// The box will autodetect the baud rate when the "IMMC" command is sent
56// to it.
57
58vrpn_Tng3::vrpn_Tng3 (const char * name,
60 const char * port,
61 int baud,
62 const int numbuttons,
63 const int numchannels):
64 vrpn_Serial_Analog(name, c, port, baud),
65 vrpn_Button_Filter(name, c),
66 _numbuttons(numbuttons),
67 _numchannels(numchannels),
68 _num_read(0)
69{
70 // Verify the validity of the parameters
72 fprintf(stderr,"vrpn_Tng3: Can only support %d buttons, not %d\n", (int)MAX_TBUTTONS, (int)_numbuttons);
74 }
76 fprintf(stderr,"vrpn_Tng3: Can only support %d analog channels, not %d\n", (int)MAX_TCHANNELS, (int)_numchannels);
78 }
79
80 // Set the parameters in the parent classes
83
84 vrpn_gettimeofday(&_timestamp, NULL); // Set watchdog now
85 bDataPacketStart = 0x55;
86
87 // Set the status of the buttons, analogs and encoders to 0 to start
89 _bufcount = 0;
90
91 // Set the mode to reset
93}
94
96{
97 int i;
98
99 for (i = 0; i < _numbuttons; i++) {
101 }
102 for (i = 0; i < _numchannels; i++) {
104 }
105}
106
107// This routine will reset the Tng3, asking it to send the requested number
108// of analogs, buttons and encoders.
109// It verifies that it can communicate with the device and checks the version of
110// the device.
111
113{
114
115 //-----------------------------------------------------------------------
116 // Set the values back to zero for all buttons and analogs
117 clear_values();
118
119 //-----------------------------------------------------------------------
120 // sending an end at this time will force the ibox into the reset mode, if it
121 // was not already. if the box is in the power up mode, nothing will happen because
122 // it 'should' be waiting to sync up the baudrate
123
124 // try to synchronize for 2 seconds
125
126 bDataPacketStart = 0x55;
127
128 if (syncDatastream (2.0)) {
129 printf("vrpn_Tng3 found\n");
130 } else {
131 return -1;
132 }
133
134 printf("TNG3B found\n");
135
137 vrpn_gettimeofday(&_timestamp, NULL); // Set watchdog now
138 return 0;
139}
140
141// This function will read characters until it has a full report, then
142// put that report into the time, analog amd button fields and call
143// the report methods on these. The time stored is that of
144// the first character received as part of the report. Each time
145// through, it gets however many characters are available.
146//
147// Reports start with the byte bDataPacketStart followed by
148// DATA_RECORD_LENGTH bytes
149//
150// If we get a report that is not valid, we assume that we have lost a
151// character or something and re-synchronize by flushing the buffer
152//
153// The routine that calls this one
154// makes sure we get a full reading often enough (ie, it is responsible
155// for doing the watchdog timing to make sure the box hasn't simply
156// stopped sending characters).
157
159{
160 int i;
161 unsigned int buttonBits = 0;
162
163 // Zero timeout, poll for any available characters
164 struct timeval timeout = {0, 0};
165
166 // Go looking for a synchronization byte. This apparently starts out
167 // as 0x55 for one record, then toggles all of the bits for the next
168 // record.
169 if (status == STATUS_SYNCING) {
170 if (1 == vrpn_read_available_characters(serial_fd, _buffer, 1, &timeout)) {
171 // if not a record start, we need to resync
172 if (_buffer[0] != bDataPacketStart) {
173 VRPN_MSG_WARNING("Resyncing");
174 return 0;;
175 }
176
177 // we got a good start byte... we're reading now
178 _num_read = 0; //< Ignore the status byte for the following record
180
181 // invert the bits for the next packet start
182 bDataPacketStart ^= 0xFF;
183 }
184 }
185
186 // we broke out.. if we're not reading, then we have nothing to do
187 if (STATUS_READING != status) {
188 return 0;
189 }
190
191 // we're reading now, get the report
192
193 // get the expected number of data record bytes
194 timeout.tv_sec = 0;
195 timeout.tv_usec = 0;
198
199 if (result < 0) {
200 VRPN_MSG_WARNING("Bad read");
202 return 0;
203 }
204
205 // If we don't have a full record, go back again.
206 _num_read += result;
208 return 0;
209 }
210
211 // parse the report here -- we got a full record.
212
213 // here is where we decode the analog stuff
214 for (i = 0; i < _numchannels; i++) {
216 vrpn_Analog::channel[i] = (unsigned short) _buffer[i];
217 }
218
219 // get the button bits and make sense of them
220
221 buttonBits = _buffer[DATA_RECORD_LENGTH-1];
222 for (i = 0; i < _numbuttons; i++) {
224 vrpn_Button::buttons[i] = static_cast<unsigned char>((buttonBits & (1 << i)) ? VRPN_BUTTON_OFF : VRPN_BUTTON_ON);
225 }
226
228 vrpn_gettimeofday(&_timestamp, NULL); // Set watchdog now
229
231 return 1;
232}
233
234void vrpn_Tng3::report_changes(vrpn_uint32 class_of_service)
235{
238
239 vrpn_Analog::report_changes(class_of_service);
241}
242
243void vrpn_Tng3::report(vrpn_uint32 class_of_service)
244{
247
248 vrpn_Analog::report(class_of_service);
250}
251
252// This routine is called each time through the server's main loop. It will
253// take a course of action depending on the current status of the TNG3,
254// either trying to reset it or trying to get a reading from it.
256{
258
259 switch(status) {
260 case STATUS_RESETTING:
261 reset();
262 break;
263
264 case STATUS_SYNCING:
265 case STATUS_READING:
266 {
267 // It turns out to be important to get the report before checking
268 // to see if it has been too long since the last report. This is
269 // because there is the possibility that some other device running
270 // in the same server may have taken a long time on its last pass
271 // through mainloop(). Trackers that are resetting do this. When
272 // this happens, you can get an infinite loop -- where one tracker
273 // resets and causes the other to timeout, and then it returns the
274 // favor. By checking for the report here, we reset the timestamp
275 // if there is a report ready (ie, if THIS device is still operating).
276 while (get_report()) {}; // Keep getting reports as long as they come
277 struct timeval current_time;
278 vrpn_gettimeofday(&current_time, NULL);
280 fprintf(stderr,"TNG3 failed to read... current_time=%ld:%ld, timestamp=%ld:%ld\n",
281 current_time.tv_sec, static_cast<long>(current_time.tv_usec),
282 _timestamp.tv_sec, static_cast<long>(_timestamp.tv_usec));
283 send_text_message("Too long since last report, resetting", current_time, vrpn_TEXT_ERROR);
285 }
286 }
287 break;
288
289 default:
290 fprintf(stderr,"vrpn_Tng3: Unknown mode (internal error)\n");
291 break;
292 }
293}
294
295
296// synchronize the data stream
297// seconds determines how long the process is permitted to continue
298
299int vrpn_Tng3::syncDatastream (double seconds) {
300
301 struct timeval miniDelay;
302 miniDelay.tv_sec = 0;
303 miniDelay.tv_usec = 50000;
304
305 unsigned long maxDelay = 1000000L * (long) seconds;
306 struct timeval start_time;
307 vrpn_gettimeofday(&start_time, NULL);
308
309 int loggedOn = 0;
310 int numRead;
311
312 if (serial_fd < 0) {
313 return 0;
314 }
315
316 // ensure that the packet start byte is valid
317 if ( bDataPacketStart != 0x55 && bDataPacketStart != 0xAA ) {
318 bDataPacketStart = 0x55;
319 }
320
322
323// vrpn_write_characters(serial_fd, (const unsigned char *)"E", 1);
324 pause (0.01);
325
326 while (!loggedOn) {
327 struct timeval current_time;
328 vrpn_gettimeofday(&current_time, NULL);
329 if (vrpn_TimevalDuration(current_time, start_time) > maxDelay ) {
330 // if we've timed out, go back unhappy
331 fprintf(stderr,"vrpn_Tng3::syncDatastream timeout expired: %d secs\n", (int)seconds);
332 return 0; // go back unhappy
333 }
334
335 // get a byte
336 if (1 != vrpn_read_available_characters(serial_fd, _buffer, 1, &miniDelay)) {
337 continue;
338 }
339 // if not a record start, skip
340 if (_buffer[0] != bDataPacketStart) {
341 continue;
342 }
343 // invert the packet start byte for the next test
344 bDataPacketStart ^= 0xFF;
345
346 // get an entire report
348 _buffer, DATA_RECORD_LENGTH, &miniDelay);
349
350 if (numRead < DATA_RECORD_LENGTH) {
351 continue;
352 }
353
354 // get the start byte for the next packet
355 if (1 != vrpn_read_available_characters(serial_fd, _buffer, 1, &miniDelay)) {
356 continue;
357 }
358
359 // if not the anticipated record start, things are not yet sync'd
360 if (_buffer[0] != bDataPacketStart) {
361 continue;
362 }
363
364 // invert the packet start byte in anticipation of normal operation
365 bDataPacketStart ^= 0xFF;
366
367 // get an entire report
369 _buffer, DATA_RECORD_LENGTH, &miniDelay);
370
371 if (numRead < DATA_RECORD_LENGTH) {
372 continue;
373 }
374
375 return 1;
376 }
377 return 0;
378}
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:39
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition vrpn_Analog.h:38
struct timeval timestamp
Definition vrpn_Analog.h:41
vrpn_int32 num_channel
Definition vrpn_Analog.h:40
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
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
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
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.
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition vrpn_Button.h:66
virtual void report_changes(void)
vrpn_int32 num_buttons
Definition vrpn_Button.h:48
struct timeval timestamp
Definition vrpn_Button.h:49
virtual void report_changes(void)
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:46
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:45
Generic connection class not specific to the transport mechanism.
virtual int get_report(void)
Definition vrpn_Tng3.C:158
struct timeval _timestamp
Definition vrpn_Tng3.h:43
unsigned _num_read
Definition vrpn_Tng3.h:38
vrpn_Tng3(const char *name, vrpn_Connection *c, const char *port, int baud=19200, const int numbuttons=8, const int numchannels=8)
Definition vrpn_Tng3.C:58
virtual void mainloop(void)
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
Definition vrpn_Tng3.C:255
virtual void clear_values(void)
Definition vrpn_Tng3.C:95
int _status
Definition vrpn_Tng3.h:34
virtual int reset(void)
Definition vrpn_Tng3.C:112
int _numbuttons
Definition vrpn_Tng3.h:35
unsigned _bufcount
Definition vrpn_Tng3.h:41
int _numchannels
Definition vrpn_Tng3.h:36
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY)
Definition vrpn_Tng3.C:243
unsigned char _buffer[512]
Definition vrpn_Tng3.h:40
#define STATUS_SYNCING
#define MAX_TIME_INTERVAL
#define STATUS_READING
#define STATUS_RESETTING
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
@ vrpn_TEXT_ERROR
#define VRPN_BUTTON_ON
#define VRPN_BUTTON_OFF
Header containing macros formerly duplicated in a lot of implementation files.
#define VRPN_MSG_WARNING(msg)
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99
#define MAX_TCHANNELS
#define MAX_TBUTTONS
Definition vrpn_Tng3.C:22
#define DATA_RECORD_LENGTH
Definition vrpn_Tng3.C:29