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>'
EDIT - for clarification.
I think reader.start_reading() should receive something Python understands, right now it receives Ruby code.
Hence, the 'found' function (or lambda) and the variables need to be translated from Ruby (Ru) to Python (Py) using PyCall.
I showed I can use PyCall to call the Py Reader object from Ru and translate the output from list to array. So the first and last step are done, great. But every interaction in between has to be bridged with PyCall as well (i.e. the 'found' func. from Ru to Py).
That, I think, is the challenge here.
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).