我有一个Simulink模型,需要从Python内部执行。我一直在使用NI VeriStand生成的C代码来编译Linux共享库,该库允许我从Python内执行仿真。
我要做的一件事是保存仿真状态(即连续和离散变量以及时钟滴答)。为此,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;
}
我一直在尝试找到一种从共享库加载的在Python中使用此功能的方法。
from ctypes import *
self._model = CDLL(model_lib)
self._lib_get_state = self._model.NIRT_GetSimState
我想找到一种将正确的数据类型传递给Python中的函数的方法。据我了解,我需要将指针传递给整数和数组。
我正在使用以下功能进行测试。我正在使用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]
我似乎获得了离散状态和连续状态数的正确值,但是具有连续状态及其名称的数组未更新。函数将输出以下内容:
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]
因此,我们可以看到函数调用没有更新数组。我认为我可能没有使用正确的数据类型来调用该函数。这是我第一次使用ctypes。
有人可以确认数据类型是否有误?正确的语法应该是什么?
谢谢。
答案 0 :(得分:0)
签出[Python 3]: ctypes - A foreign function library for Python。
Python 代码有几处错误:
您没有为self._lib_get_state
声明 argtypes (和 restype )。这可能会导致:
对于应该接收多于一个值的参数指针(它们都是指针),您可以传递一组指针。例如: contStatesNames 是char*
,而您传递的c_wchar_p * 1
是(wchar_t*)[1]
,它与char*
完全不同(绝对不是您输入的字符串期待如此)
该功能的设计方式也很差:
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)
(ctypes.c_double * 1)(-999.99)
-> ctypes.c_double(-999.99)
)。无论如何,我没有改变基于函数体,这是使它工作的方法(这是一种方法-不用说我没有测试代码):
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
答案 1 :(得分:0)
事实证明,错误不在于Python代码,而在于我调用C代码的方式。第一次调用该函数是为了获取要分配的数组的长度,然后是第二次将变量复制到数组中。
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)