简体   繁体   English

如何为 NI VeriStand 共享库传递带有 ctypes 的数组

[英]How to pass arrays with ctypes for NI VeriStand shared library

I have a Simulink model that I need to execute from within Python.我有一个需要从 Python 中执行的 Simulink 模型。 I have been using C code generated with NI VeriStand to compile a Linux shared library that allows me to execute my simulation from within Python.我一直在使用 NI VeriStand 生成的 C 代码来编译 Linux 共享库,该库允许我从 Python 中执行我的模拟。

One of the things I want to do is save the state of my simulation (ie, continuous and discrete variables, and clock ticks).我想做的一件事是保存我的模拟状态(即,连续和离散变量,以及时钟滴答)。 The C source exported by VeriStand provides a function called NIRT_GetSimState for such a purpose. VeriStand 导出的 C 源代码为此提供了一个名为 NIRT_GetSimState 的函数。

DLL_EXPORT int32_t NIRT_GetSimState(int32_t* numContStates, char  * contStatesNames, double* contStates, int32_t* numDiscStates, char
  * discStatesNames, double* discStates, int32_t* numClockTicks, char
  * clockTicksNames, int32_t* clockTicks)
{
  int32_t count = 0;
  int32_t idx = 0;
  if ((numContStates != NULL) && (numDiscStates != NULL) && (numClockTicks !=
       NULL)) {
    if (*numContStates < 0 || *numDiscStates < 0 || *numClockTicks < 0) {
      *numContStates = 1;
      *numDiscStates = 0;
      *numClockTicks = NUMST - TID01EQ;
      return NI_OK;
    }
  }

  if ((contStates != NULL) && (contStatesNames != NULL)) {
    idx = 0;
    contStates[idx] = NIRT_GetValueByDataType(&(electric_motor_X.speed), 0, 0, 0);
    strcpy(contStatesNames + (idx++ * 100), "speed");
  }

  if ((clockTicks != NULL) && (clockTicksNames != NULL)) {
    clockTicks[0] = S->Timing.clockTick0;
    strcpy(clockTicksNames, "clockTick0");
  }

  UNUSED_PARAMETER(count);
  UNUSED_PARAMETER(idx);
  return NI_OK;
}

I have been trying to find a way to use this function in Python, as loaded from a shared library.我一直试图找到一种在 Python 中使用这个函数的方法,它是从共享库加载的。

from ctypes import *
self._model = CDLL(model_lib)
self._lib_get_state = self._model.NIRT_GetSimState

I want to find a way to pass the correct data types to the function in Python.我想找到一种方法将正确的数据类型传递给 Python 中的函数。 From what I understand, I need to pass pointers to integers and to arrays.据我了解,我需要传递指向整数和数组的指针。

I am using the following function for testing.我正在使用以下功能进行测试。 I am creating variables and arrays using ctypes.我正在使用 ctypes 创建变量和数组。

def _get_state(self):
    numContStates = c_int(-999)
    contStatesNames = (c_wchar_p*1)('a')
    contStates = (c_double*1)(-999.99)
    numDiscStates = c_int(-999)
    discStatesNames = (c_wchar_p*1)('a')
    discStates = (c_double*1)(-999.99)
    numClockTicks = c_int(-999)
    clockTicksNames = (c_wchar_p*1)('a')
    clockTicks = (c_int*1)(-999)
    self._lib_get_state(byref(numContStates), byref(contStatesNames), byref(contStates), byref(numDiscStates), byref(discStatesNames),
        byref(discStates), byref(numClockTicks), byref(clockTicksNames), byref(clockTicks))
    print('Number of continuous states: ', numContStates.value)
    print('Number of discrete states: ', numDiscStates.value)
    print('Number of clock ticks: ', numClockTicks.value)
    print('Names of continuous states: ', list(contStatesNames)) # Expecting ['speed']
    print('Values of continuous states: ', list(contStates)) # Expecting [0.0]

I seem to be getting the right values for the number of discrete and continuous states but the arrays with the continuous states and their names are not updated.我似乎得到了离散和连续状态数量的正确值,但具有连续状态的数组及其名称没有更新。 This is what the function prints:这是函数打印的内容:

Number of continuous states:  1
Number of discrete states:  0
Number of clock ticks:  1
Names of continuous states:  ['a']
Values of continuous states:  [-999.99]

So, we can see that the function call did not update the arrays.所以,我们可以看到函数调用没有更新数组。 I think that I'm probably not using the right data types to call the function.我认为我可能没有使用正确的数据类型来调用该函数。 This is my first time using ctypes.这是我第一次使用 ctypes。

Could someone confirm if there is a mistake in the data types?有人可以确认数据类型是否有错误吗? and what the correct syntax should be?正确的语法应该是什么?

Thank you.谢谢你。

Check out [Python 3]: ctypes - A foreign function library for Python .查看[Python 3]:ctypes - Python 的外部函数库

There are a couple of things wrong with the Python code: Python代码有几个问题:

Also the way the function is designed is poor:该功能的设计方式也很差:

DLL_EXPORT int32_t NIRT_GetSimState(int32_t *numContStates, char *contStatesNames,
                                    double *contStates, int32_t *numDiscStates,
                                    char *discStatesNames, double *discStates,
                                    int32_t *numClockTicks, char *clockTicksNames,
                                    int32_t *clockTicks)
  • Way too many arguments.太多的争论。 This case would call for a container ( struct ) to encapsulate them这种情况需要一个容器( struct )来封装它们
  • It doesn't seem to check the pointers (1 st for nullptr ), then only dumps the data to their addresses, never wondering whether if it went out of bounds.它似乎没有检查指针(第一个nullptr ),然后只将数据转储到它们的地址,从不怀疑它是否越界。 That means that the function caller must allocate enough space for all buffers, in order for the info to fit in. Usually, this kind of situation is handled:这意味着函数调用者必须为所有缓冲区分配足够的空间,以便信息适合。通常,处理这种情况:
    • Via another argument that specifies the buffer size, and if the function has more data to place in the buffer, it either doesn't do anything either fills the buffer, and at the end returns an error通过另一个指定缓冲区大小的参数,如果函数有更多数据要放入缓冲区,它要么不做任何事情要么填充缓冲区,最后返回错误
    • The functions allocates the memory (it's the caller responsibility to deallocate it)函数分配内存(释放它是调用者的责任)
  • I see that for many fields an array of length 1 is declared.我看到对于许多字段,声明了长度为 1 的数组。 If only one element is supposed to be returned then don't make it an array ( (ctypes.c_double * 1)(-999.99) -> ctypes.c_double(-999.99) ).如果应该只返回一个元素,则不(ctypes.c_double * 1)(-999.99)数组( (ctypes.c_double * 1)(-999.99) -> ctypes.c_double(-999.99) )。 Anyway, I didn't change it反正我没改

Based on the function body, here's how you could get it working (this is one way - needless to say that I didn't test the code):基于函数体,这里是如何让它工作(这是一种方法 - 不用说我没有测试代码):

self._lib_get_state.argtypes = [
    ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_char),
    ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_int32),
    ctypes.POINTER(ctypes.c_char), ctypes.POINTER(ctypes.c_double),
    ctypes.POINTER(ctypes.c_int32), ctypes.POINTER(ctypes.c_char),
    ctypes.POINTER(ctypes.c_int32),
]
self._lib_get_state.restype = ctypes.c_int32

numContStates = ctypes.c_int32(-999)
contStatesNames = ctypes.create_string_buffer(106)  # The "speed" text is copied 100 characters from beginning
contStates = ctypes.c_double(-999.99)
numDiscStates = ctypes.c_int32(-999)
discStatesNames = (ctypes.c_char * 1)(b"a")
discStates = (ctypes.c_double * 1)(-999.99)
numClockTicks = ctypes.c_int32(-999)
clockTicksNames = ctypes.create_string_buffer(11)  # Equivalent to: ctypes.c_char * 11
clockTicks = (ctypes.c_int32 * 1)(-999)

result = self._lib_get_state(
    ctypes.byref(numContStates), ctypes.cast(contStatesNames, ctypes.POINTER(ctypes.c_char)),
    ctypes.byref(contStates), ctypes.byref(numDiscStates),
    ctypes.cast(discStatesNames, ctypes.POINTER(ctypes.c_char)), ctypes.cast(discStates, ctypes.POINTER(ctypes.c_double)),
    ctypes.byref(numClockTicks), ctypes.cast(clockTicksNames, ctypes.POINTER(ctypes.c_char)),
    ctypes.byref(clockTicks))


clockTicksNames.value  # This is how to get the string out

It turns out that the mistake was not on the Python code but on the way I was calling the C code.事实证明,错误不是在 Python 代码上,而是在我调用 C 代码的方式上。 The function was meant to be called a first time to get the lengths of the arrays to allocate, and a second time to copy the variables into the arrays.第一次调用该函数以获取要分配的数组的长度,第二次调用以将变量复制到数组中。

self._lib_get_state = self._model.NIRT_GetSimState

self.num_cont_states = c_int(-999)
self.num_disc_states = c_int(-999)
self.num_clock_ticks = c_int(-999)

self._lib_get_state(byref(self.num_cont_states), byref(c_char()), byref(c_double()),
    byref(self.num_disc_states), byref(c_char()), byref(c_double()),
    byref(self.num_clock_ticks), byref(c_char()), byref(c_int()))

self._cont_states_names = create_string_buffer(b'\000' * (100*self.num_cont_states.value))
self._cont_states = (c_double*self.num_cont_states.value)()
self._disc_states_names = create_string_buffer(b'\000' * (100*self.num_disc_states.value))
self._disc_states = (c_double*self.num_disc_states.value)()
self._clock_ticks_names = create_string_buffer(b'\000' * (100*self.num_clock_ticks.value))
self._clock_ticks = (c_int*self.num_clock_ticks.value)()

self._lib_get_state(byref(self.num_cont_states), self._cont_states_names,
    self._cont_states, byref(self.num_disc_states), self._disc_states_names,
    self._disc_states, byref(self.num_clock_ticks), self._clock_ticks_names,
    self._clock_ticks)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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