I have a Simulink model that I need to execute from within Python. 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.
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.
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.
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. 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.
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.
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 .
There are a couple of things wrong with the Python code:
You didn't declare argtypes (and restype ) for self._lib_get_state
. Here's what that can lead to:
For argument pointers (they are all pointers) that are supposed to receive more that one value you pass an array of pointers. Eg: contStatesNames which is a char*
you pass a c_wchar_p * 1
which is (wchar_t*)[1]
which is totally different from char*
(definitely not the string you're expecting it to be)
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)
(ctypes.c_double * 1)(-999.99)
-> ctypes.c_double(-999.99)
). Anyway, I didn't change itBased 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. 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)
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.