Обмен данными между несколькими s-функциями c mex
Я реализую несколько с-функций. Они должны полагаться на одни и те же указатели и переменные, не зависящие от текущей s-функции.
По сути, я хочу создать все переменные и указатели в одной s-функции "setup" (внутри mdlInitialize), а затем иметь возможность использовать эти переменные указатели в различных s-функциях в их функции mdlOutputs. Каждая s-функция будет написана на c.
Я не смог найти ни одной полезной подсказки в справке по математике. Есть ли у вас какие-либо идеи? Благодарю.
1 ответ
Это можно сделать несколькими способами, хотя ни один из них не является лучшим подходом.
Один из подходов состоит в том, чтобы определить все в dll и загрузить его каждой из S-функций. Это обрисовано в общих чертах в вопросе, как разделить структуру C среди функций C S?,
Другой (и мой предпочтительный) подход заключается в создании пользовательского типа данных, который представляет собой C-структуру, содержащую все общие данные, и которая передается как сигнал между S-функциями в модели. Это изложено в разделе Using Opaque Data Types in C S-Functions
документа Настроить пользовательские типы данных.
Документ показывает различные (относительно простые) вещи, которые необходимо выполнить в S-функции, которая создает пользовательскую структуру. В этой S-функции MdlOutputs
метод, пользовательская структура будет тогда просто сделать вывод блока обычным способом. Например, если пользовательская структура, содержащая ваши данные, определяется как,
typedef struct{
real_T sig0;
real_T sig1;
}myStruct;
Затем в mdlInitializeSizes
тебе нужно что-то вроде
myStruct tmp;
/* Register the user-defined data types */
id = ssRegisterDataType(S, "customDataType");
if(id == INVALID_DTYPE_ID) return;
/* Set the size of the user-defined data type */
status = ssSetDataTypeSize(S, id, sizeof(tmp));
if(status == 0) return;
/* Set the zero representation */
tmp.sig0 = 0;
tmp.sig1 = 0;
status = ssSetDataTypeZero(S, id, &tmp);
И вывести это как сигнал, в mdlOutputs
метод у вас будет что-то вроде
myStruct *pY0 = (myStruct *)ssGetOutputPortSignal(S, 0);
pY0[0].sig0 = 'value of this param';
pY0[0].sig1 = 'value of this param';
Тогда в mdlInitializeSizes
любой S-функции, которая должна использовать этот сигнал вам нужно
DTypeId id;
id = ssRegisterDataType(S, "customDataType");
if(id == INVALID_DTYPE_ID) return;
который затем дает вам доступ к пользовательской структуре в любом из других методов, используя,
myStruct **uPtrs = (myStruct **) ssGetInputPortSignalPtrs(S,0);
Затем к элементам структуры получают доступ обычным способом,
firstVar = uPtrs[0]->sig0;
secondVar = uPtrs[0]->sig1;
Основным недостатком этого подхода является то, что модель не может быть использована при генерации кода (с использованием Simulink Coder).
Самый простой способ обмена данными между S-функциями в сгенерированном коде - просто передать их через глобальные переменные, если ваши S-функции не встроены.
Поскольку вы получаете ошибку с уже определенными переменными, я полагаю, вы компилируете сгенерированный код на целевой платформе. В данном случае это вызвано дублированием определений - глобальные переменные должны быть определены только в S-функции "setup" и объявлены с ключевым словом extern во всех других S-функциях (это просто C в целевой).
Когда S-функция компилируется с mex в среде Simulink, она не может быть статически связана с другими S-функциями. Хотя можно связать несколько S-функций с одним и тем же внешним исходным файлом C, в Simulink, вероятно, будет несколько экземпляров одних и тех же данных, созданных независимо для каждой S-функции. Поскольку S-функции компилируются и связываются независимо в среде Simulink, определение общих глобальных переменных в каждой S-функции не имеет смысла - вы не можете извлекать их, поскольку они не были бы определены для компоновщика (вызываемого mex).
Очевидный недостаток связывания глобальных объектов с целью состоит в том, что вы не можете использовать среду Simulink для отладки функциональности, которая зависит от общих данных. Но если это не основная функциональность (например, некоторая дополнительная регистрация), ее можно условно включить с флагом RT внутри ваших S-функций, чтобы он появлялся только в сгенерированном коде, позволяя проверить основную функциональность в Simulink.