#python #c #ruby #ffi
Вопрос:
ПРАВКА: Документация по соответствующим библиотечным методам!
Полное раскрытие — я почти ничего не знаю о C/C и буквально ничего не знаю об указателях, буферах и всех этих забавных вещах. Извините, если это глупый вопрос, но я работал над этим в течение нескольких часов и вообще не продвинулся далеко.
Таким образом, у меня есть внешняя библиотека C, на которую я ссылаюсь из сценария Ruby. Есть куча различных функций, к которым мне нужен доступ, и у меня есть несколько простых, которые работают, но я борюсь с более сложной функцией, с которой мне нужно работать.
Хорошая часть — у меня есть рабочий пример кода на Python.
Проблема, с которой я сталкиваюсь, связана со всей буферной стороной использования FFI — я не нашел много соответствующих примеров того, как это работает, и я немного борюсь.
Вот функция, которую я хочу воспроизвести в Ruby с помощью FFI:
def getActiveID32():
"""
Will return a tuple consisting number of bits
read and actual data. Minimum 8 byte of data will
be returned.
"""
rawData = ""
buffer_size = ctypes.c_short(32);
pcproxlib.GetActiveID32.restype = ctypes.c_short
# create a buffer of given size to pass it
# to get the raw data.
raw_data_tmp = (ctypes.c_ubyte * buffer_size.value)()
#as per documentation 250 millisecond sleep is required
# to get the raw data.
time.sleep(250/1000.0)
nbBits = pcproxlib.GetActiveID32(raw_data_tmp , buffer_size)
bytes_to_read = int((nbBits 7) / 8) ;
# will read at least 8 bytes
if bytes_to_read < 8:
bytes_to_read = 8
for i in range(0 , bytes_to_read):
temp_buf = "X " % raw_data_tmp[i]
rawData = temp_buf rawData
return (nbBits , rawData)
Вот пример кода, который я использую для простых функций (например BeepNow
, но не для этой более сложной функции, getActiveID32
.
Включен код, с которым я играл, но он явно слишком упрощен и не работает.
require 'ffi'
module PCProxLib
extend FFI::Library
ffi_lib File.expand_path('lib/32/libhidapi-hidraw.so.0')
ffi_lib File.expand_path('lib/32/libpcProxAPI.so')
attach_function :usbConnect, [], :short
attach_function :getPartNumberString, [], :string
attach_function :getActiveID32, [:string, :int], :int
attach_function :getActiveID, [], :int
attach_function :BeepNow, [:int, :bool], :bool
end
puts PCProxLib.usbConnect()
puts PCProxLib.BeepNow(3, false)
sleep 0.25
buffer = ""
puts PCProxLib.getActiveID32(buffer, 64)
puts buffer
Thanks heaps for any help 🙂
EDIT:
Основываясь на комментарии @matt, я изменил код следующим образом:
require 'ffi'
module PCProxLib
extend FFI::Library
ffi_lib File.expand_path('lib/32/libhidapi-hidraw.so.0')
ffi_lib File.expand_path('lib/32/libpcProxAPI.so')
attach_function :usbConnect, [], :short
attach_function :USBDisconnect, [], :short
attach_function :getActiveID32, [:pointer, :int], :int
end
PCProxLib.usbConnect()
puts PCProxLib.getPartNumberString() // verifying connection to reader
def read_card
sleep 0.5
buffer = FFI::MemoryPointer.new(:uint8, 32)
bits_written = PCProxLib.getActiveID32(buffer, 32)
puts bits_written
puts buffer.read_bytes(32)
end
20.times { read_card }
PCProxLib.USBDisconnect()
Когда я сканирую карту (пока код зацикливается), bits_written
значение подскакивает с 0 до 32, что кажется хорошим. Однако buffer.read_bytes(32)
значение всегда равно нулю.
Я пробовал некоторые другие методы определения памяти (например read
, read_string
и т. Д.), Но получил те же результаты.
Если я попробую .inspect
buffer.read_bytes(32)
это сделать , то получу:
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
…что интересно? Это одно и то же, независимо от того, сканирую я карту или нет.
Комментарии:
1. Если буфер содержит нули после вызова, это означает, что функция записывает нули. Но мне интересно, какую задержку вы добавляете, конечно, было бы разумно, чтобы это произошло после звонка, чтобы дать ему время для получения и записи данных?
2. @мэтт да согласился. Повторная задержка — хороший момент. Я попытался изменить задержку, к сожалению, тот же результат. Я обратился к производителю за разъяснениями, надеюсь, они смогут пролить некоторый свет…
3. @matt Я добавил ссылку на соответствующую документацию в верхней части вопроса, это может помочь прояснить ситуацию?
Ответ №1:
Похоже, что функция getActiveID32
имеет подпись, которая выглядит примерно так
int getActiveID32(uint8_t *buffer, int size)
и вы могли бы использовать его, сначала создав буфер для записи, а затем передавая указатель на этот буфер и его размер в качестве аргументов при его вызове, что-то вроде:
// Define BUFFER_SIZE first.
uint8_t *buffer = malloc(BUFFER_SIZE);
int bits_written = getActiveID32(buffer, BUFFER_SIZE);
// There are now bits_written bits in buffer for you to use.
// You will need to call free on buffer when you are finished.
С помощью FFI Ruby вы можете сделать это с помощью указателя. Сначала объявите функцию для приема указателя (строка похожа на указатель, но в этом случае нам нужно использовать указатель напрямую).:
attach_function :getActiveID32, [:pointer, :int], :int
Теперь, когда вы хотите вызвать его, вам нужно сначала создать a MemoryPointer
нужного размера и передать его в качестве параметра функции. Затем FFI преобразует это в указатель на выделенную память при вызове функции C.
# Assuming you want a 32 byte buffer.
buffer = FFI::MemoryPointer.new(:uint8, 32)
bits_written = PCProxLib.getActiveID32(buffer, 32)
Теперь buffer
будут содержаться данные, записанные функцией. Вы можете получить доступ к этим данным с помощью методов MemoryPointer
, например, скопировать эти данные в рубиновую (двоичную) строку, которую вы можете использовать buffer.read_bytes(32)
.
MemoryPointer
также будет обрабатывать освобождение выделенной памяти, когда она будет собрана в мусор.
Комментарии:
1. Пожалуйста, обратитесь к правке в вопросе, потому что форматирование комментариев сегодня кажется мне недоступным. Большое спасибо за вашу помощь, я очень признателен.