I would like to access a Python package in Ruby using PyCall.
The non-blocking function in the Python code below activates a callback.
Could you help me implement the callback?
#!/usr/bin/env python
# start_reading.py
import time
import mercury
def found(tag):
print(tag.epc.decode()) # Check check
# print(tags[1].epc.decode())
reader = mercury.Reader("tmr:///dev/ttyUSB0")
reader.start_reading(found, on_time=250, off_time=0)
time.sleep(0.5)
reader.stop_reading()
Output start_reading.py:
b'001853000000000000000012'
b'34155CACB400000000000125'
b'34155CACB400000000000125'
b'34155CACB400000000000125'
b'001853000000000000000012'
b'34155CACB400000000000125'
b'001853000000000000000012'
b'34155CACB400000000000125'
b'34155CACB400000000000125'
This pertains an api for ThingMagic RFID readers, which is a Python wrapper for the original api written in C.
From the docs:
reader.start_reading(callback, on_time=250, off_time=0)
Starts asynchronous reading. It returns immediately and begins a sequence of reads or a continuous read. The results are passed to the callback. The reads are repeated until the
reader.stop_reading()method is called
- callback(TagReadData) will be invoked for every tag detected
- on_time sets the duration, in milliseconds, for the reader to be actively querying
- off_time duration, in milliseconds, for the reader to be quiet while querying
As follows the parts I got working using a single read function. First the Python code, then Ruby.
#!/usr/bin/env python
# read.py
import mercury
reader = mercury.Reader("tmr:///dev/ttyUSB0")
data = reader.read()
print(data)
print(f"type: {type(data)}")
Output read.py:
[EPC(b'34155CACB400000000000125'), EPC(b'001853000000000000000012')]
type: <class 'list'>
#!/usr/bin/env ruby
# read.rb
require 'pycall/import'
include PyCall::Import
pyimport :mercury
reader = mercury.Reader.new("tmr:///dev/ttyUSB0")
python_data = reader.read()
puts "python_data: #{python_data}"
puts "python class: #{python_data.class}"
puts ""
ruby_data = python_data.to_ary
puts "ruby_data: #{ruby_data}"
puts "ruby class: #{ruby_data.class}"
Output read.rb:
python_data: [EPC(b'34155CACB400000000000125'), EPC(b'001853000000000000000012')]
python class: <class 'list'>
ruby_data: [EPC(b'34155CACB400000000000125'), EPC(b'001853000000000000000012')]
ruby class: Array
The above works, as you can see.
The solution for the callback should be an improvement upon the following code.
#!/usr/bin/env ruby
# start_reading.rb
require 'pycall/import'
include PyCall::Import
pyimport :mercury
def found(tag)
puts tag.epc
end
reader = mercury.Reader.new("tmr:///dev/ttyUSB0")
reader.start_reading(found, on_time=250, off_time=0) # What should I do here?
sleep(0.5)
reader.stop_reading()
Current output start_reading.rb
start_reading.rb:7:in `found': wrong number of arguments (given 0, expected 1) (ArgumentError)
from start_reading.rb:14:in `<main>'
reader.start_reading(lambda tag: print(tag.epc))- have you tried the same thing?reader.start_reading(lambda tag: found(tag), on_time=250, off_time=0)reader.start_reading(->(tag){ puts tag.epc }, 250, 0)orreader.start_reading(method(:found), 250, 0).../pycall/pytypeobject_wrapper.rb:24:in `call_object': <class 'TypeError'>: Resource temporarily unavailable (PyCall::PyError). I think whatever is passed to the reader.start_reading() function should be something Python understands. Hence, I think I need a way to transform the found function (or the lambda) from Ruby to Python using PyCall - the same is true for the other two variables (which are optional so I omitted them in my test).