ant-wireless 0.1.0.pre.20210617213631

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,233 @@
1
+ /*
2
+ * callbacks.c - ANT callback handling
3
+ * $Id$
4
+ *
5
+ * This contains code adapted from Kim Burge Strand's Library-of-Massive-Fun-And-Overjoy
6
+ * project, namely the code that calls back into Ruby from ANT callbacks. Used under
7
+ * the conditions of the WTFPL. For more info on how it works, see the associated article:
8
+ *
9
+ * https://www.burgestrand.se//articles/asynchronous-callbacks-in-ruby-c-extensions/
10
+ *
11
+ * Authors:
12
+ * * Michael Granger <ged@FaerieMUD.org>
13
+ *
14
+ */
15
+
16
+ #include "ant_ext.h"
17
+
18
+
19
+ /*
20
+ * Three globals to allow for Ruby/C-thread communication:
21
+ * - mutex & condition to synchronize access to callback_queue
22
+ * - callback_queue to store actual callback data in
23
+ * Be careful with the functions that manipulate the callback
24
+ * queue; they must do so in the protection of a mutex.
25
+ */
26
+ pthread_mutex_t rant_callback_mutex = PTHREAD_MUTEX_INITIALIZER;
27
+ pthread_cond_t rant_callback_cond = PTHREAD_COND_INITIALIZER;
28
+ rant_callback_t *rant_callback_queue = NULL;
29
+
30
+ typedef struct callback_waiting_t callback_waiting_t;
31
+ struct callback_waiting_t {
32
+ rant_callback_t *callback;
33
+ bool abort;
34
+ };
35
+
36
+
37
+ /*
38
+ * Use this function to add a callback node onto the global
39
+ * callback queue.
40
+ * Do note that we are adding items to the front of the linked
41
+ * list, and as such events will always be handled by most recent
42
+ * first. To remedy this, add to the end of the queue instead.
43
+ */
44
+ static void
45
+ callback_queue_push( rant_callback_t *callback )
46
+ {
47
+ callback->next = rant_callback_queue;
48
+ rant_callback_queue = callback;
49
+ }
50
+
51
+
52
+ /*
53
+ * Use this function to pop off a callback node from the
54
+ * global callback queue. Returns NULL if queue is empty.
55
+ */
56
+ static rant_callback_t *
57
+ callback_queue_pop(void)
58
+ {
59
+ rant_callback_t *callback = rant_callback_queue;
60
+
61
+ if ( callback )
62
+ {
63
+ rant_callback_queue = callback->next;
64
+ }
65
+ return callback;
66
+ }
67
+
68
+
69
+ /*
70
+ * Queue a +callback+ for handling by Ruby. Blocks until it's handled.
71
+ */
72
+ bool
73
+ rant_callback( rant_callback_t *callback )
74
+ {
75
+ pthread_mutex_init( &callback->mutex, NULL );
76
+ pthread_cond_init( &callback->cond, NULL );
77
+
78
+ callback->handled = false;
79
+
80
+ // Put callback data in global callback queue
81
+ pthread_mutex_lock( &rant_callback_mutex );
82
+ callback_queue_push( callback );
83
+ pthread_mutex_unlock( &rant_callback_mutex );
84
+
85
+ // Notify waiting Ruby thread that we have callback data
86
+ pthread_cond_signal( &rant_callback_cond );
87
+
88
+ // Wait for callback to be handled
89
+ pthread_mutex_lock( &callback->mutex );
90
+ while ( callback->handled == false )
91
+ {
92
+ pthread_cond_wait( &callback->cond, &callback->mutex );
93
+ }
94
+ pthread_mutex_unlock( &callback->mutex );
95
+
96
+ // Clean up
97
+ pthread_mutex_destroy( &callback->mutex );
98
+ pthread_cond_destroy( &callback->cond );
99
+
100
+ return callback->rval;
101
+ }
102
+
103
+
104
+ /*
105
+ * Executed for each callback notification; what we receive
106
+ * are the callback parameters. The job of this method is to:
107
+ * 1. Convert callback parameters into Ruby values
108
+ * 2. Call the appropriate callback with said parameters
109
+ * 3. Convert the Ruby return value into a C value
110
+ * 4. Hand over the C value to the C callback
111
+ */
112
+ static VALUE
113
+ handle_callback( void *cb )
114
+ {
115
+ rant_callback_t *callback = (rant_callback_t *)cb;
116
+ int state = 0;
117
+ VALUE rval;
118
+
119
+ // callback->fn( callback->data );
120
+ rval = rb_protect( callback->fn, (VALUE)callback->data, &state );
121
+
122
+ // tell the callback that it has been handled, we are done
123
+ pthread_mutex_lock( &callback->mutex );
124
+
125
+ callback->handled = true;
126
+ callback->rval = RTEST( rval ) ? true : false;
127
+
128
+ pthread_cond_signal( &callback->cond );
129
+ pthread_mutex_unlock( &callback->mutex );
130
+
131
+ if ( state ) {
132
+ rb_jump_tag( state );
133
+ }
134
+
135
+ return rval;
136
+ }
137
+
138
+
139
+ /*
140
+ * Wait for the next callback in the global callback queue.
141
+ */
142
+ static void *
143
+ wait_for_callback_signal( void *w_ptr )
144
+ {
145
+ callback_waiting_t *waiting = (callback_waiting_t*) w_ptr;
146
+
147
+ pthread_mutex_lock( &rant_callback_mutex );
148
+
149
+ // abort signal is used when ruby wants us to stop waiting
150
+ while ( waiting->abort == false && (waiting->callback = callback_queue_pop()) == NULL )
151
+ {
152
+ pthread_cond_wait( &rant_callback_cond, &rant_callback_mutex );
153
+ }
154
+
155
+ pthread_mutex_unlock( &rant_callback_mutex );
156
+
157
+ return NULL;
158
+ }
159
+
160
+
161
+ /*
162
+ * Unblocking function: tell the callback thread to abort if Ruby says it's
163
+ * shutdown time.
164
+ */
165
+ static void
166
+ stop_waiting_for_callback_signal( void *w_ptr )
167
+ {
168
+ callback_waiting_t *waiting = (callback_waiting_t*) w_ptr;
169
+
170
+ pthread_mutex_lock( &rant_callback_mutex );
171
+
172
+ waiting->abort = true;
173
+
174
+ pthread_cond_signal( &rant_callback_cond );
175
+ pthread_mutex_unlock( &rant_callback_mutex );
176
+ }
177
+
178
+
179
+ /*
180
+ * Callback handler thread routine; loops until told to abort. Each loop:
181
+ *
182
+ * - Release the GVL
183
+ * - Wait on a signal on the global condition variable with an unblock function
184
+ * that tells it to abort.
185
+ * - If there's a callback, create a child thread to run it.
186
+ *
187
+ */
188
+ static VALUE
189
+ callback_thread( void *unused )
190
+ {
191
+ callback_waiting_t waiting = {
192
+ .callback = NULL,
193
+ .abort = false
194
+ };
195
+
196
+ while ( waiting.abort == false )
197
+ {
198
+ // release the GIL while waiting for a callback notification
199
+ rb_thread_call_without_gvl( wait_for_callback_signal, &waiting,
200
+ stop_waiting_for_callback_signal, &waiting );
201
+
202
+ // if ruby wants us to abort, this will be NULL
203
+ if ( waiting.callback )
204
+ {
205
+ // rant_log( "debug", "Starting a callback thread." );
206
+ rb_thread_create( handle_callback, (void *)waiting.callback );
207
+ }
208
+ }
209
+
210
+ return Qnil;
211
+ }
212
+
213
+
214
+
215
+ /*
216
+ * Start a Thread which will wait for ANT callbacks and dispatch them when they arrive.
217
+ */
218
+ void
219
+ rant_start_callback_thread()
220
+ {
221
+ // ThreadGroup isn't a public symbol, so have to look it up
222
+ VALUE cThGroup = rb_define_class( "ThreadGroup", rb_cObject );
223
+ VALUE thread_group = rb_class_new_instance( 0, NULL, cThGroup );
224
+ VALUE callback_dispatcher = rb_thread_create( callback_thread, (void *)NULL );
225
+
226
+ rb_funcallv( thread_group, rb_intern("add"), 1, &callback_dispatcher );
227
+ rb_ivar_set( rant_mAnt, rb_intern("@callback_threads"), thread_group );
228
+ rb_ivar_set( rant_mAnt, rb_intern("@callback_dispatcher"), callback_dispatcher );
229
+ rb_attr( rb_singleton_class(rant_mAnt), rb_intern("callback_threads"), 1, 0, 0 );
230
+ rb_attr( rb_singleton_class(rant_mAnt), rb_intern("callback_dispatcher"), 1, 0, 0 );
231
+ }
232
+
233
+
@@ -0,0 +1,524 @@
1
+ /*
2
+ * channel.c - Ant::Channel class
3
+ * $Id$
4
+ *
5
+ * Authors:
6
+ * * Michael Granger <ged@FaerieMUD.org>
7
+ *
8
+ */
9
+
10
+ #include "ant_ext.h"
11
+
12
+ VALUE rant_cAntChannel;
13
+
14
+ VALUE rant_mAntDataUtilities;
15
+
16
+ static void rant_channel_free( void * );
17
+ static void rant_channel_mark( void * );
18
+
19
+
20
+ static const rb_data_type_t rant_channel_datatype_t = {
21
+ .wrap_struct_name = "Ant::Channel",
22
+ .function = {
23
+ .dmark = rant_channel_mark,
24
+ .dfree = rant_channel_free,
25
+ },
26
+ .data = NULL,
27
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
28
+ };
29
+
30
+
31
+
32
+ /*
33
+ * Free function
34
+ */
35
+ static void
36
+ rant_channel_free( void *ptr )
37
+ {
38
+ if ( ptr ) {
39
+ rant_channel_t *channel = (rant_channel_t *)ptr;
40
+ ANT_AssignChannelEventFunction( channel->channel_num, NULL, NULL );
41
+ ANT_UnAssignChannel( channel->channel_num );
42
+
43
+ channel->callback = Qnil;
44
+
45
+ xfree( ptr );
46
+ ptr = NULL;
47
+ }
48
+ }
49
+
50
+
51
+ /*
52
+ * Mark function
53
+ */
54
+ static void
55
+ rant_channel_mark( void *ptr )
56
+ {
57
+ rant_channel_t *channel = (rant_channel_t *)ptr;
58
+ rb_gc_mark( channel->callback );
59
+ }
60
+
61
+
62
+ /*
63
+ * Alloc function
64
+ */
65
+ static VALUE
66
+ rant_channel_alloc( VALUE klass )
67
+ {
68
+ rant_channel_t *ptr;
69
+
70
+ VALUE rval = TypedData_Make_Struct( klass, rant_channel_t, &rant_channel_datatype_t, ptr );
71
+ ptr->callback = Qnil;
72
+
73
+ return rval;
74
+ }
75
+
76
+
77
+
78
+ /*
79
+ * Fetch the data pointer and check it for sanity.
80
+ */
81
+ rant_channel_t *
82
+ rant_get_channel( VALUE self )
83
+ {
84
+ rant_channel_t *ptr;
85
+
86
+ if ( !IsChannel(self) ) {
87
+ rb_raise( rb_eTypeError, "wrong argument type %s (expected Ant::Channel)",
88
+ rb_class2name(CLASS_OF( self )) );
89
+ }
90
+
91
+ ptr = DATA_PTR( self );
92
+ assert( ptr );
93
+
94
+ return ptr;
95
+ }
96
+
97
+
98
+ /*
99
+ * Clear the registry after channel have been reset.
100
+ */
101
+ void
102
+ rant_channel_clear_registry()
103
+ {
104
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
105
+ rb_hash_clear( registry );
106
+ }
107
+
108
+
109
+
110
+ /*
111
+ * call-seq:
112
+ * channel.initialize
113
+ *
114
+ * Set up the channel.
115
+ *
116
+ */
117
+ static VALUE
118
+ rant_channel_init( VALUE self, VALUE channel_number, VALUE channel_type, VALUE network_number,
119
+ VALUE extended_options )
120
+ {
121
+ rant_channel_t *ptr = rant_get_channel( self );
122
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
123
+
124
+ ptr->channel_num = NUM2USHORT( channel_number );
125
+ MEMZERO( ptr->buffer, unsigned char, MESG_MAX_SIZE );
126
+
127
+ rb_iv_set( self, "@channel_type", channel_type );
128
+ rb_iv_set( self, "@network_number", network_number );
129
+ rb_iv_set( self, "@extended_options", extended_options );
130
+
131
+ rb_hash_aset( registry, channel_number, self );
132
+
133
+ return self;
134
+ }
135
+
136
+
137
+ /*
138
+ * call-seq:
139
+ * channel.channel_number -> integer
140
+ *
141
+ * Return the channel number assigned to the Channel.
142
+ *
143
+ */
144
+ static VALUE
145
+ rant_channel_channel_number( VALUE self )
146
+ {
147
+ rant_channel_t *ptr = rant_get_channel( self );
148
+
149
+ return INT2FIX( ptr->channel_num );
150
+ }
151
+
152
+
153
+ /*
154
+ * call-seq:
155
+ * channel.set_channel_id( device_number, device_type, transmission_type, timeout=0 )
156
+ *
157
+ * Set the channel ID using the combination of the +device_number+,
158
+ * +device_type+, and +transmission_type+. If the assignment hasn't been set in
159
+ * +timeout+ seconds, aborts and returns +nil+.
160
+ *
161
+ */
162
+ static VALUE
163
+ rant_channel_set_channel_id( int argc, VALUE *argv, VALUE self )
164
+ {
165
+ rant_channel_t *ptr = rant_get_channel( self );
166
+ VALUE device_number, device_type, transmission_type, timeout;
167
+ unsigned short usDeviceNumber;
168
+ unsigned char ucDeviceType,
169
+ ucTransmissionType;
170
+ unsigned int ulResponseTime = 0;
171
+ bool result;
172
+
173
+ rb_scan_args( argc, argv, "31", &device_number, &device_type, &transmission_type, &timeout );
174
+
175
+ usDeviceNumber = NUM2USHORT( device_number );
176
+ ucDeviceType = NUM2USHORT( device_type );
177
+ ucTransmissionType = NUM2USHORT( transmission_type );
178
+
179
+ if ( RTEST(timeout) )
180
+ ulResponseTime = NUM2UINT( timeout );
181
+
182
+ result = ANT_SetChannelId_RTO( ptr->channel_num, usDeviceNumber, ucDeviceType,
183
+ ucTransmissionType, ulResponseTime );
184
+
185
+ if ( !result ) {
186
+ rb_raise( rb_eRuntimeError, "Failed to set the channel id." );
187
+ }
188
+
189
+ return Qtrue;
190
+ }
191
+
192
+
193
+ // ANT_SetChannelPeriod_RTO(UCHAR ucANTChannel_, USHORT usMesgPeriod_, ULONG ulResponseTime_);
194
+ // ANT_SetChannelSearchTimeout_RTO(UCHAR ucANTChannel_, UCHAR ucSearchTimeout_, ULONG ulResponseTime_);
195
+ // ANT_SetChannelRFFreq_RTO(UCHAR ucANTChannel_, UCHAR ucRFFreq_, ULONG ulResponseTime_);
196
+
197
+
198
+ /*
199
+ * call-seq:
200
+ * channel.open( tineout=0 )
201
+ *
202
+ * Open the channel. If it hasn't completed within +timeout+ seconds, raises a RuntimeError.
203
+ */
204
+ static VALUE
205
+ rant_channel_open( int argc, VALUE *argv, VALUE self )
206
+ {
207
+ rant_channel_t *ptr = rant_get_channel( self );
208
+ VALUE timeout;
209
+ unsigned int ulResponseTime = 0;
210
+
211
+ rb_scan_args( argc, argv, "01", &timeout );
212
+
213
+ if ( RTEST(timeout) )
214
+ ulResponseTime = NUM2UINT( timeout );
215
+
216
+ if ( !ANT_OpenChannel_RTO( ptr->channel_num, ulResponseTime ) ) {
217
+ rb_raise( rb_eRuntimeError, "Failed to open the channel." );
218
+ }
219
+
220
+ return Qtrue;
221
+ }
222
+
223
+
224
+ /*
225
+ * call-seq:
226
+ * channel.close
227
+ *
228
+ * Close the channel and remove it from the registry.
229
+ *
230
+ */
231
+ static VALUE
232
+ rant_channel_close( int argc, VALUE *argv, VALUE self )
233
+ {
234
+ rant_channel_t *ptr = rant_get_channel( self );
235
+ VALUE timeout;
236
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
237
+ unsigned int ulResponseTime = 0;
238
+
239
+ rb_scan_args( argc, argv, "01", &timeout );
240
+
241
+ if ( RTEST(timeout) )
242
+ ulResponseTime = NUM2UINT( timeout );
243
+
244
+ rant_log_obj( self, "info", "Closing channel %d (with timeout %d).", ptr->channel_num, ulResponseTime );
245
+ if ( !ANT_CloseChannel_RTO( ptr->channel_num, ulResponseTime ) ) {
246
+ rb_raise( rb_eRuntimeError, "Failed to close the channel." );
247
+ }
248
+ rant_log_obj( self, "info", "Channel %d closed.", ptr->channel_num );
249
+
250
+ rb_hash_delete( registry, INT2FIX( ptr->channel_num ) );
251
+
252
+ return Qtrue;
253
+ }
254
+
255
+
256
+ /*
257
+ * call-seq:
258
+ * channel.closed? -> true or false
259
+ *
260
+ * Returns +true+ if the channel has been closed; i.e., if it's not longer the
261
+ * registered channel for its channel number.
262
+ *
263
+ */
264
+ static VALUE
265
+ rant_channel_closed_p( VALUE self )
266
+ {
267
+ rant_channel_t *ptr = rant_get_channel( self );
268
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
269
+ VALUE channel = rb_hash_lookup( registry, INT2FIX( ptr->channel_num ) );
270
+
271
+ return self == channel ? Qfalse : Qtrue;
272
+ }
273
+
274
+
275
+ /*
276
+ * Event callback functions
277
+ */
278
+
279
+ struct on_event_call {
280
+ unsigned char ucANTChannel;
281
+ unsigned char ucEvent;
282
+ };
283
+
284
+
285
+ /*
286
+ * Handle the event callback -- Ruby side.
287
+ */
288
+ static VALUE
289
+ rant_channel_call_event_callback( VALUE callPtr )
290
+ {
291
+ struct on_event_call *call = (struct on_event_call *)callPtr;
292
+ VALUE registry = rb_iv_get( rant_cAntChannel, "@registry" );
293
+ VALUE channel = rb_hash_fetch( registry, INT2FIX(call->ucANTChannel) );
294
+ rant_channel_t *ptr = rant_get_channel( channel );
295
+ VALUE rb_callback = ptr->callback;
296
+ VALUE rval = Qnil;
297
+
298
+ if ( RTEST(rb_callback) ) {
299
+ VALUE args[3];
300
+
301
+ args[0] = INT2FIX( call->ucANTChannel );
302
+ args[1] = INT2FIX( call->ucEvent );
303
+ args[2] = rb_enc_str_new( (char *)ptr->buffer, MESG_MAX_SIZE, rb_ascii8bit_encoding() );
304
+
305
+ rval = rb_funcallv_public( rb_callback, rb_intern("call"), 3, args );
306
+ }
307
+
308
+ MEMZERO( ptr->buffer, unsigned char, MESG_MAX_SIZE );
309
+
310
+ return rval;
311
+ }
312
+
313
+
314
+ /*
315
+ * Handle the event callback -- C side.
316
+ */
317
+ static BOOL
318
+ rant_channel_on_event_callback( unsigned char ucANTChannel, unsigned char ucEvent )
319
+ {
320
+ rant_callback_t callback;
321
+ struct on_event_call call;
322
+
323
+ call.ucANTChannel = ucANTChannel;
324
+ call.ucEvent = ucEvent;
325
+
326
+ callback.data = &call;
327
+ callback.fn = rant_channel_call_event_callback;
328
+
329
+ return rant_callback( &callback );
330
+ }
331
+
332
+
333
+ /*
334
+ * call-seq:
335
+ * channel.on_event {|channel_num, event_id, data| ... }
336
+ *
337
+ * Set up a callback for events on the receiving channel.
338
+ *
339
+ */
340
+ static VALUE
341
+ rant_channel_on_event( int argc, VALUE *argv, VALUE self )
342
+ {
343
+ rant_channel_t *ptr = rant_get_channel( self );
344
+ VALUE callback = Qnil;
345
+
346
+ rb_scan_args( argc, argv, "0&", &callback );
347
+
348
+ if ( !RTEST(callback) ) {
349
+ rb_raise( rb_eLocalJumpError, "block required, but not given" );
350
+ }
351
+
352
+ rant_log_obj( self, "debug", "Channel event callback is: %s", RSTRING_PTR(rb_inspect(callback)) );
353
+ ptr->callback = callback;
354
+
355
+ ANT_AssignChannelEventFunction( ptr->channel_num, rant_channel_on_event_callback, ptr->buffer );
356
+
357
+ return Qtrue;
358
+ }
359
+
360
+
361
+ /*
362
+ * call-seq:
363
+ * channel.send_burst_transfer( data )
364
+ *
365
+ * Send the given +data+ as one or more burst packets.
366
+ *
367
+ */
368
+ static VALUE
369
+ rant_channel_send_burst_transfer( VALUE self, VALUE data )
370
+ {
371
+ rant_channel_t *ptr = rant_get_channel( self );
372
+ unsigned char *data_s;
373
+ long data_len = RSTRING_LEN( data );
374
+ unsigned short usNumDataPackets = data_len / 8,
375
+ remainingBytes = data_len % 8;
376
+
377
+ data_s = ALLOC_N( unsigned char, data_len );
378
+ strncpy( (char *)data_s, StringValuePtr(data), data_len );
379
+
380
+ // Pad it to 8-byte alignment
381
+ if ( remainingBytes ) {
382
+ int pad_bytes = (8 - remainingBytes);
383
+ REALLOC_N( data_s, unsigned char, data_len + pad_bytes );
384
+ memset( data_s + data_len, 0, pad_bytes );
385
+
386
+ usNumDataPackets += 1;
387
+ }
388
+
389
+ VALUE data_string = rb_enc_str_new( (char *)data_s, usNumDataPackets * 8, rb_ascii8bit_encoding() );
390
+ VALUE hexdump = rb_funcall( rant_mAntDataUtilities, rb_intern("hexdump"), 1, data_string );
391
+
392
+ rant_log_obj( self, "debug", "Sending burst packets:\n%s", RSTRING_PTR(hexdump) );
393
+ if ( !ANT_SendBurstTransfer(ptr->channel_num, data_s, usNumDataPackets) ) {
394
+ rb_raise( rb_eRuntimeError, "failed to send burst transfer." );
395
+ }
396
+
397
+ return Qtrue;
398
+ }
399
+
400
+
401
+ /*
402
+ * call-seq:
403
+ * channel.send_acknowledged_data( data )
404
+ *
405
+ * Send the given +data+ as an acknowledged transmission. The +data+ cannot be longer
406
+ * than 8 bytes in length.
407
+ *
408
+ */
409
+ static VALUE
410
+ rant_channel_send_acknowledged_data( VALUE self, VALUE data )
411
+ {
412
+ rant_channel_t *ptr = rant_get_channel( self );
413
+ UCHAR aucTempBuffer[] = {0, 0, 0, 0, 0, 0, 0, 0};
414
+
415
+ StringValue( data );
416
+ if ( RSTRING_LEN(data) > 8 ) {
417
+ rb_raise( rb_eArgError, "Data can't be longer than 8 bytes." );
418
+ }
419
+ strncpy( (char *)aucTempBuffer, StringValuePtr(data), RSTRING_LEN(data) );
420
+
421
+ ANT_SendAcknowledgedData( ptr->channel_num, aucTempBuffer );
422
+
423
+ return Qtrue;
424
+ }
425
+
426
+
427
+ /*
428
+ * call-seq:
429
+ * channel.send_broadcast_data( data )
430
+ *
431
+ * Send the given +data+ as a broadcast transmission. The +data+ cannot be longer
432
+ * than 8 bytes in length.
433
+ *
434
+ */
435
+ static VALUE
436
+ rant_channel_send_broadcast_data( VALUE self, VALUE data )
437
+ {
438
+ rant_channel_t *ptr = rant_get_channel( self );
439
+ UCHAR aucTempBuffer[8] = {0, 0, 0, 0, 0, 0, 0, 0,};
440
+
441
+ StringValue( data );
442
+ if ( RSTRING_LEN(data) > 8 ) {
443
+ rb_raise( rb_eArgError, "Data can't be longer than 8 bytes." );
444
+ }
445
+ strncpy( (char *)aucTempBuffer, StringValuePtr(data), RSTRING_LEN(data) );
446
+
447
+ ANT_SendBroadcastData( ptr->channel_num, aucTempBuffer );
448
+
449
+ return Qtrue;
450
+ }
451
+
452
+
453
+ /*
454
+ * call-seq:
455
+ * channel.set_channel_rf_freq( frequency )
456
+ *
457
+ * Set the ANT RF +frequency+.
458
+ *
459
+ */
460
+ static VALUE
461
+ rant_channel_set_channel_rf_freq( VALUE self, VALUE frequency )
462
+ {
463
+ rant_channel_t *ptr = rant_get_channel( self );
464
+ unsigned short ucRFFreq = NUM2USHORT( frequency );
465
+
466
+ if ( ucRFFreq > 124 ) {
467
+ rb_raise( rb_eArgError, "frequency must be between 0 and 124." );
468
+ }
469
+
470
+ ANT_SetChannelRFFreq( ptr->channel_num, ucRFFreq );
471
+
472
+ return Qtrue;
473
+ }
474
+
475
+
476
+ void
477
+ init_ant_channel()
478
+ {
479
+ #ifdef FOR_RDOC
480
+ rb_cData = rb_define_class( "Data" );
481
+ rant_mAnt = rb_define_module( "Ant" );
482
+ #endif
483
+
484
+ /*
485
+ * Document-module: Ant::Channel
486
+ *
487
+ * An assigned ANT channel object.
488
+ *
489
+ */
490
+ rant_cAntChannel = rb_define_class_under( rant_mAnt, "Channel", rb_cObject );
491
+ rb_iv_set( rant_cAntChannel, "@registry", rb_hash_new() );
492
+
493
+ rant_mAntDataUtilities = rb_define_module_under( rant_mAnt, "DataUtilities" );
494
+
495
+ rb_define_alloc_func( rant_cAntChannel, rant_channel_alloc );
496
+ rb_define_protected_method( rant_cAntChannel, "initialize", rant_channel_init, 4 );
497
+
498
+ rb_define_method( rant_cAntChannel, "channel_number", rant_channel_channel_number, 0 );
499
+
500
+ rb_attr( rant_cAntChannel, rb_intern("channel_type"), 1, 0, 0 );
501
+ rb_attr( rant_cAntChannel, rb_intern("network_number"), 1, 0, 0 );
502
+ rb_attr( rant_cAntChannel, rb_intern("extended_options"), 1, 0, 0 );
503
+
504
+ rb_define_method( rant_cAntChannel, "set_channel_id", rant_channel_set_channel_id, -1 );
505
+ // rb_define_method( rant_cAntChannel, "set_channel_period",
506
+ // rant_channel_set_channel_period, -1 );
507
+ // rb_define_method( rant_cAntChannel, "set_channel_search_timeout",
508
+ // rant_channel_set_channel_search_timeout, -1 );
509
+ rb_define_method( rant_cAntChannel, "set_channel_rf_freq", rant_channel_set_channel_rf_freq, 1 );
510
+
511
+ rb_define_method( rant_cAntChannel, "open", rant_channel_open, -1 );
512
+ rb_define_method( rant_cAntChannel, "close", rant_channel_close, -1 );
513
+ rb_define_method( rant_cAntChannel, "closed?", rant_channel_closed_p, 0 );
514
+
515
+ rb_define_method( rant_cAntChannel, "send_burst_transfer", rant_channel_send_burst_transfer, 1 );
516
+ rb_define_method( rant_cAntChannel, "send_acknowledged_data", rant_channel_send_acknowledged_data, 1 );
517
+ rb_define_method( rant_cAntChannel, "send_broadcast_data", rant_channel_send_broadcast_data, 1 );
518
+
519
+ rb_define_method( rant_cAntChannel, "on_event", rant_channel_on_event, -1 );
520
+
521
+ rb_require( "ant/channel" );
522
+ }
523
+
524
+