Проблемы с получением возвращаемого значения из библиотеки C, завернутой в FFI в Ruby, например, с использованием типов файлов Python

#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. Пожалуйста, обратитесь к правке в вопросе, потому что форматирование комментариев сегодня кажется мне недоступным. Большое спасибо за вашу помощь, я очень признателен.