简体   繁体   中英

Loading c DLL with python, problems with pointer

Currently I have C++ loading DLL. I need replace C++ code with python. My problems are:

  1. in callback function device_ID1_callback, all values seems empty, i'm guessing i did not use pointer correctly.
  2. after call device_get_info, all values are 0, I suppose get some values none zero back. I have tried anything I can think of for weeks but with very little luck.

To simplified the problem, here's partial of my code. Thanks for your time and help!!

in my lib.h file, i have

typedef unsigned int DeviceHandler;
typedef struct {
        unsigned int fpga_version;
}DeviceInfo_t;
typedef struct {
        unsigned int check_id;
        float distance[256];
}MeasureResult_t;
DLLEPXORT int EXCALL device_add(DeviceHandler* outHandler, char* device_ip, MeasureModeCallback callback);
DLLEPXORT void EXCALL device_get_info(DeviceHandler handler, DeviceInfo_t* p_device_info);

in sample C++ file: """

void device_ID1_callback(const void *out,unsigned int out_num){
    MeasureResult_t *ptr = (MeasureResult_t *)out;
    printf("[ChechID:0x%x] %d pack's data\n",ptr[0].check_id,out_num);
}
void demo_callback_mode(){
    int ret;
    DeviceHandler device_handler;
    DeviceInfo_t device_info;

    ret = device_add(&device_handler,"192.168.1.2",&device_ID1_callback);
    device_get_info(device_handler,&device_info);

    printf("[FPGA] version : %d\n", device_info.fpga_version);
}

""" *end of c++ *

Here's my python code: """

import ctypes   as c

class MeasureResult_t(c.Structure):
    _fields_  = [
            ('check_id', c.c_int),
            ('distance[256]', c.c_float)]
    
class DeviceInfo_t(c.Structure):
    _fields_  = [
            ('fpga_version', c.c_int)
            ]
    
def device_ID1_callback(out, out_num):
    print("---enter device call back function---")
    print(dir(out))
    print("out: ",out.contents)
    #print(dir(out))
    print("out_num:",out_num)
    print("---exit device call back function---\n\n")
    return 0

_dev = c.CDLL("./OPSensor/osp_lidar")

T_device_handler = c.c_int
T_device_handler_ptr = c.POINTER(T_device_handler)

_dev.device_add.argtypes = [T_device_handler_ptr, c.c_char_p]
_dev.device_add.restype = c.c_int


device_handler = c.c_int()
ip_val = c.c_char_p("192.168.1.2".encode('utf-8'))

    
out = MeasureResult_t()
out_num = c.c_int()


CMPFUNC_t = c.CFUNCTYPE(None, c.POINTER(MeasureResult_t), c.c_int)
MeasureModeCallback = CMPFUNC_t(device_ID1_callback)

                   
ret = _dev.device_add(c.byref(device_handler), (ip_val), MeasureModeCallback(c.byref(out), out_num))

    
_dev.device_get_info.argtypes = [T_device_handler_ptr, c.POINTER(DeviceInfo_t)]
_dev.device_get_info.restype = c.c_void_p # assume it returns C int

p_device_info = DeviceInfo_t()
#_dev.device_get_info(c.byref(device_handler), c.byref(p_device_info)) # does not work
_dev.device_get_info((device_handler), c.byref(p_device_info)) #does not work either

print(device_handler) # I have correct device_handler value 
print(p_device_info.fpga_version) # the value i got is 0, does seem right

"""

Here's my attempt at a minimal reproducible example . I implemented dummy functions that demonstrate the callback you described:

// lib.c
#define DLLEXPORT __declspec(dllexport)
#define EXCALL

typedef unsigned int DeviceHandler;

typedef struct {
        unsigned int fpga_version;
} DeviceInfo_t;

typedef struct {
        unsigned int check_id;
        float distance[256];
} MeasureResult_t;

typedef void (*MeasureModeCallback)(MeasureResult_t*, unsigned int);

DLLEXPORT int EXCALL device_add(DeviceHandler* outHandler, char* device_ip, MeasureModeCallback callback) {
    *outHandler = 123;  // dummy device ID
    MeasureResult_t m;  // some fake measurement results
    m.check_id = 456;
    for(int i = 0; i < 256; ++i)
        m.distance[i] = (float)(i * .25);
    callback(&m, 789);  // call the callback
    return 1;
}

DLLEXPORT void EXCALL device_get_info(DeviceHandler handler, DeviceInfo_t* p_device_info) {
    p_device_info->fpga_version = handler * 2; // fake fpga version
}

To create a callback in Python, assign the CFUNCTYPE prototype to the callback type, decorate the callback function with that type, and use that type in the callback argument definition, and the actual function name when passing it as an argument.

Also note the that float distance[256] is declared in Python as c.c_float * 256 to create an array and the differences in the .argtypes/.restype attributes for the functions. device_get_info takes a DeviceHandler , not a c.POINTER(DeviceHandler) for example.

# test.py
import ctypes as c

class MeasureResult_t(c.Structure):
    _fields_  = (('check_id', c.c_uint),
                 ('distance', c.c_float * 256))
    def __repr__(self):  # defines how to display this class
        return f'MeasureResult_t(check_id={self.check_id}, distance=[{self.distance[0]}, ..., {self.distance[255]}])'

class DeviceInfo_t(c.Structure):
    _fields_  = ('fpga_version', c.c_uint),
    def __repr__(self):  # defines how to display this class
        return f'DeviceInfo_t(fpga_version={self.fpga_version})'

# Declare the callback type
MeasureModeCallback = c.CFUNCTYPE(None, c.POINTER(MeasureResult_t), c.c_uint)

DeviceHandler = c.c_uint

# apply the decorator so this function can be called from C
@MeasureModeCallback
def device_ID1_callback(out, out_num):
    print('---enter device call back function---')
    print('out: ',out.contents)
    print('out_num:',out_num)
    print('---exit device call back function---')

_dev = c.CDLL('./lib')
# Use the argument type
_dev.device_add.argtypes = c.POINTER(DeviceHandler), c.c_char_p, MeasureModeCallback
_dev.device_add.restype = c.c_int
_dev.device_get_info.argtypes = DeviceHandler, c.POINTER(DeviceInfo_t)
_dev.device_get_info.restype = None

device_handler = DeviceHandler()
ip_val = b'192.168.1.2'

# Use the callback function name when calling the function
ret = _dev.device_add(c.byref(device_handler), ip_val, device_ID1_callback)

device_info = DeviceInfo_t()
_dev.device_get_info(device_handler, c.byref(device_info))

print(f'{device_handler.value=}')
print(f'{device_info=}')

Output. Note the classes know how to display themselves and the fake data agrees with my implementation:

---enter device call back function---
out:  MeasureResult_t(check_id=456, distance=[0.0, ..., 63.75])
out_num: 789
---exit device call back function---
device_handler.value=123
device_info=DeviceInfo_t(fpga_version=246)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM