Class: Tinkerforge::Device

Inherits:
Object
  • Object
show all
Defined in:
lib/tinkerforge/ip_connection.rb

Constant Summary collapse

RESPONSE_EXPECTED_INVALID_FUNCTION_ID =
0
RESPONSE_EXPECTED_ALWAYS_TRUE =

getter

1
RESPONSE_EXPECTED_ALWAYS_FALSE =

callback

2
RESPONSE_EXPECTED_TRUE =

setter

3
RESPONSE_EXPECTED_FALSE =

setter, default

4

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uid, ipcon) ⇒ Device

Creates the device object with the unique device ID uid and adds it to the IPConnection ipcon.



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
# File 'lib/tinkerforge/ip_connection.rb', line 198

def initialize(uid, ipcon)
  @uid = Base58.decode uid

  if @uid > 0xFFFFFFFF
    # convert from 64bit to 32bit
    value1 = @uid & 0xFFFFFFFF
    value2 = (@uid >> 32) & 0xFFFFFFFF

    @uid  = (value1 & 0x00000FFF)
    @uid |= (value1 & 0x0F000000) >> 12
    @uid |= (value2 & 0x0000003F) << 16
    @uid |= (value2 & 0x000F0000) << 6
    @uid |= (value2 & 0x3F000000) << 2
  end

  @api_version = [0, 0, 0]

  @ipcon = ipcon

  @request_mutex = Mutex.new

  @response_expected = Array.new(256, RESPONSE_EXPECTED_INVALID_FUNCTION_ID)
  @response_expected[IPConnection::FUNCTION_ENUMERATE] = RESPONSE_EXPECTED_ALWAYS_FALSE
  @response_expected[IPConnection::CALLBACK_ENUMERATE] = RESPONSE_EXPECTED_ALWAYS_FALSE

  @expected_response_function_id = 0
  @expected_response_sequence_number = 0

  @response_mutex = Mutex.new
  @response_condition = ConditionVariable.new
  @response_queue = Queue.new

  @callback_formats = {}
  @registered_callbacks = {}

  @ipcon.devices[@uid] = self # FIXME: use a weakref here
end

Instance Attribute Details

#callback_formatsObject

Returns the value of attribute callback_formats.



193
194
195
# File 'lib/tinkerforge/ip_connection.rb', line 193

def callback_formats
  @callback_formats
end

#expected_response_function_idObject

Returns the value of attribute expected_response_function_id.



191
192
193
# File 'lib/tinkerforge/ip_connection.rb', line 191

def expected_response_function_id
  @expected_response_function_id
end

#expected_response_sequence_numberObject

Returns the value of attribute expected_response_sequence_number.



192
193
194
# File 'lib/tinkerforge/ip_connection.rb', line 192

def expected_response_sequence_number
  @expected_response_sequence_number
end

#registered_callbacksObject

Returns the value of attribute registered_callbacks.



194
195
196
# File 'lib/tinkerforge/ip_connection.rb', line 194

def registered_callbacks
  @registered_callbacks
end

#uidObject

Returns the value of attribute uid.



190
191
192
# File 'lib/tinkerforge/ip_connection.rb', line 190

def uid
  @uid
end

Instance Method Details

#dequeue_response(message) ⇒ Object

internal



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/tinkerforge/ip_connection.rb', line 403

def dequeue_response(message)
  response = nil

  @response_mutex.synchronize {
    @response_condition.wait @response_mutex, @ipcon.timeout

    if @response_queue.empty?
      raise TimeoutException, message
    end

    response = @response_queue.pop
  }

  response
end

#enqueue_response(response) ⇒ Object

internal



395
396
397
398
399
400
# File 'lib/tinkerforge/ip_connection.rb', line 395

def enqueue_response(response)
  @response_mutex.synchronize {
    @response_queue.push response
    @response_condition.signal
  }
end

#get_api_versionObject

Returns the API version (major, minor, revision) of the bindings for this device.



238
239
240
# File 'lib/tinkerforge/ip_connection.rb', line 238

def get_api_version
  @api_version
end

#get_response_expected(function_id) ⇒ Object

Returns the response expected flag for the function specified by the function_id parameter. It is true if the function is expected to send a response, false otherwise.

For getter functions this is enabled by default and cannot be disabled, because those functions will always send a response. For callback configuration functions it is enabled by default too, but can be disabled via the set_response_expected function. For setter functions it is disabled by default and can be enabled.

Enabling the response expected flag for a setter function allows to detect timeouts and other error conditions calls of this setter as well. The device will then send a response for this purpose. If this flag is disabled for a setter function then no response is send and errors are silently ignored, because they cannot be detected.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/tinkerforge/ip_connection.rb', line 257

def get_response_expected(function_id)
  if function_id < 0 or function_id > 255
    raise ArgumentError, "Function ID #{function_id} out of range"
  end

  flag = @response_expected[function_id]

  if flag == RESPONSE_EXPECTED_INVALID_FUNCTION_ID
    raise ArgumentError, "Invalid function ID #{function_id}"
  end

  if flag == RESPONSE_EXPECTED_ALWAYS_TRUE or \
     flag == RESPONSE_EXPECTED_TRUE
    true
  else
    false
  end
end

#send_request(function_id, request_data, request_format, response_length, response_format) ⇒ Object

internal



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/tinkerforge/ip_connection.rb', line 328

def send_request(function_id, request_data, request_format,
                 response_length, response_format)
  response = nil

  if request_data.length > 0
    payload = pack request_data, request_format
  else
    payload = ''
  end

  header, response_expected, sequence_number = \
    @ipcon.create_packet_header self, 8 + payload.length, function_id
  request = header + payload

  if response_expected
    packet = nil

    @request_mutex.synchronize {
      @expected_response_function_id = function_id
      @expected_response_sequence_number = sequence_number

      begin
        @ipcon.send_request request

        while true
          packet = dequeue_response "Did not receive response in time for function ID #{function_id}"

          if function_id == get_function_id_from_data(packet) and \
             sequence_number == get_sequence_number_from_data(packet)
            # ignore old responses that arrived after the timeout expired, but before setting
            # expected_response_function_id and expected_response_sequence_number back to None
            break
          end
        end
      ensure
        @expected_response_function_id = 0
        @expected_response_sequence_number = 0
      end
    }

    error_code = get_error_code_from_data(packet)

    if error_code == 0
      # no error
    elsif error_code == 1
      raise NotSupportedException, "Got invalid parameter for function ID #{function_id}"
    elsif error_code == 2
      raise NotSupportedException, "Function ID #{function_id} is not supported"
    else
      raise NotSupportedException, "Function ID #{function_id} returned an unknown error"
    end

    if response_length > 0
      response = unpack packet[8..-1], response_format

      if response.length == 1
        response = response[0]
      end
    end
  else
    @ipcon.send_request request
  end

  response
end

#set_response_expected(function_id, response_expected) ⇒ Object

Changes the response expected flag of the function specified by the function_id parameter. This flag can only be changed for setter (default value: false) and callback configuration functions (default value: true). For getter functions it is always enabled and callbacks it is always disabled.

Enabling the response expected flag for a setter function allows to detect timeouts and other error conditions calls of this setter as well. The device will then send a response for this purpose. If this flag is disabled for a setter function then no response is send and errors are silently ignored, because they cannot be detected.



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/tinkerforge/ip_connection.rb', line 287

def set_response_expected(function_id, response_expected)
  if function_id < 0 or function_id > 255
    raise ArgumentError, "Function ID #{function_id} out of range"
  end

  flag = @response_expected[function_id]

  if flag == RESPONSE_EXPECTED_INVALID_FUNCTION_ID
    raise ArgumentError, "Invalid function ID #{function_id}"
  end

  if flag == RESPONSE_EXPECTED_ALWAYS_TRUE or \
     flag == RESPONSE_EXPECTED_ALWAYS_FALSE
    raise ArgumentError, "Response Expected flag cannot be changed for function ID #{function_id}"
  end

  if response_expected
    @response_expected[function_id] = RESPONSE_EXPECTED_TRUE
  else
    @response_expected[function_id] = RESPONSE_EXPECTED_FALSE
  end
end

#set_response_expected_all(response_expected) ⇒ Object

Changes the response expected flag for all setter and callback configuration functions of this device at once.



312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/tinkerforge/ip_connection.rb', line 312

def set_response_expected_all(response_expected)
  if response_expected
    flag = RESPONSE_EXPECTED_TRUE
  else
    flag = RESPONSE_EXPECTED_FALSE
  end

  for function_id in 0..255
    if @response_expected[function_id] == RESPONSE_EXPECTED_TRUE or \
       @response_expected[function_id] == RESPONSE_EXPECTED_FALSE
      @response_expected[function_id] = flag
    end
  end
end