Руководство программиста
Глава 2. Создание моделей блоков
§2.6. Динамические переменные
§2.6.5. Работа со сложными динамическими переменными
Рассматривается работа с динамическими переменными сложных типов: матрицами, структурами и т.п. Приводится пример, в котором два блока передают друг другу матрицу вещественных чисел через динамическую переменную в корневой подсистеме.
Во всех приведенных примерах (§2.6.2, §2.6.3, §2.6.4) модели блоков работали с простыми динамическими переменными типа double. В работе с динамическими переменными сложных типов (строками, матрицами, структурами) нет принципиальных отличий. Получив указатель на область данных переменной (поле Data структуры RDS_DYNVARLINK) и удостоверившись, что он не равен NULL, можно обращаться к данным динамической переменной так же, как если бы это была обычная статическая переменная.
Для примера создадим модели двух блоков, один из которых будет записывать поступившую ему на вход матрицу вещественных чисел в созданную им динамическую переменную «DynMatr» в корневой подсистеме, а другой – считывать эту матрицу из динамической переменной, передавать ее на свой выход и вычислять сумму ее элементов (рис. 45). Фактически эти блоки представляют собой упрощенные аналоги рассмотренных ранее блока-передатчика и блока-приемника, передающие матрицу вместо вещественного числа и не имеющие возможности изменить имя динамической переменной, связывающей их.
Рис. 45. Передача матрицы через динамическую переменную
Начнем с модели блока, записывающего матрицу в динамическую переменную. Помимо двух обязательных сигналов, этот блок будет иметь единственный вход «X» типа «матрица double»:
| Смещение | Имя | Тип | Размер | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|---|---|
| 0 | Start | Сигнал | 1 | Вход | ✓ | 1 |
| 1 | Ready | Сигнал | 1 | Выход | 0 | |
| 2 | X | Матрица double | 16 | Вход | ✓ |
Как и в других моделях, работающих с единственной динамической переменной, указатель на ее структуру подписки будет запоминаться в поле BlockData структуры RDS_BLOCKDATA, поскольку как таковой личной области данных у блока не будет, и этот указатель можно использовать как угодно. Модель блока будет достаточно простой:
extern "C" __declspec(dllexport) int RDSCALL TestDynMatrCreate(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define pX ((void **)(pStart+2*RDS_VSZ_S)) // Вспомогательная переменная – указатель на структуру подписки RDS_PDYNVARLINK Link; switch(CallMode) { // Инициализация блока case RDS_BFM_INIT: // Создание динамической переменной Link=rdsCreateAndSubscribeDV(RDS_DVROOT, // В корневой "DynMatr", // Имя переменной "MD", // Тип TRUE, // Запрет удаления NULL); // Без нач.знач. // Запомнить указатель на структуру подписки BlockData->BlockData=Link; break; // Очистка case RDS_BFM_CLEANUP: // Запомненный указатель на структуру подписки Link=(RDS_PDYNVARLINK)BlockData->BlockData; // Удалить переменную rdsDeleteDVByLink(Link); break; // Проверка типа статических переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSMD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Такт расчета case RDS_BFM_MODEL: // Запомненный указатель на структуру подписки Link=(RDS_PDYNVARLINK)BlockData->BlockData; // Если переменная существует, копировать входную матрицу // в матрицу динамической переменной if(Link!=NULL && Link->Data!=NULL) { // Копирование X в динамическую переменную rdsCopyVarArray(Link->Data,pX); // Уведомление подписчиков rdsNotifyDynVarSubscribers(Link); } break; } return RDS_BFR_DONE; // Отмена макроопределений #undef pX #undef Ready #undef Start #undef pStart } //=========================================
При вызове модели в режиме RDS_BFM_INIT в корневой подсистеме будет создана динамическая переменная «DynMatr» типа «MD», то есть «матрица double», указатель на структуру подписки которой будет запомнен в переменной BlockData->BlockData. При вызове модели в режиме RDS_BFM_CLEANUP эта переменная будет уничтожена. Эти действия уже несколько раз описывались в предыдущих примерах и останавливаться на них подробнее нет смысла. При вызове модели для выполнения такта расчета запомненный в BlockData->BlockData указатель на структуру подписки присваивается вспомогательной переменной Link, и, если этот указатель существует (Link!=NULL) и у динамической переменной есть данные (Link->Data!=NULL), вызывается сервисная функция rdsCopyVarArray, копирующая данные матрицы, на которую ссылается указатель pX, в матрицу, на которую ссылается указатель Link->Data – так вход блока копируется в динамическую переменную. Функция rdsCopyVarArray может работать как со статическими матрицами, так и с динамическими – все функции и макросы, которые работают с данными статических переменных, могут с тем же успехом работать и с данными динамических, поскольку их представление в памяти не отличается.
Теперь создадим модель блока, который будет считывать данные из динамической переменной «DynMatr». Блок будет выдавать полученную матрицу на выход «Y», а сумму ее элементов – на выход «s»:
| Смещение | Имя | Тип | Размер | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|---|---|
| 0 | Start | Сигнал | 1 | Вход | ✓ | 1 |
| 1 | Ready | Сигнал | 1 | Выход | 0 | |
| 2 | Y | Матрица double | 16 | Выход | ||
| 18 | s | double | 8 | Выход | 0 |
Сигналу «Start» необходимо присвоить начальное значение 1 – это самый простой способ заставить модель блока выполниться в первом такте расчета. Также желательно выключить для блока запуск каждый такт. Модель будет передавать динамическую матрицу на выход и суммировать ее элементы и при каждом изменении переменной «DynMatr», о котором ее информирует блок с моделью TestDynMatrCreate при помощи сервисной функции rdsNotifyDynVarSubscribers, и при выполнении такта расчета. На самом деле запуск модели в режиме RDS_BFM_MODEL произойдет всего один раз – при первом запуске расчета, из-за того, что сигнал «Start» в этот момент будет иметь значение 1. После первого же запуска значение этого сигнала будет сброшено, и, поскольку у блока нет входов, а запуск каждый такт отключен, в дальнейшем модель будет вызываться только при изменении динамической переменной.
extern "C" __declspec(dllexport) int RDSCALL TestDynMatrGet(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define pY ((void **)(pStart+2*RDS_VSZ_S)) #define s (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_M))) // Вспомогательная переменная – указатель на структуру подписки RDS_PDYNVARLINK Link; switch(CallMode) { // Инициализация блока case RDS_BFM_INIT: // Подписка на динамическую переменную Link=rdsSubscribeToDynamicVar(RDS_DVROOT, // В корневой "DynMatr", // Имя "MD", // Тип FALSE); // Без поиска // Запомнить указатель на структуру подписки BlockData->BlockData=Link; break; // Очистка case RDS_BFM_CLEANUP: // Запомненный указатель на структуру подписки Link=(RDS_PDYNVARLINK)BlockData->BlockData; // Прекратить подписку rdsUnsubscribeFromDynamicVar(Link); break; // Проверка типа статических переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSMDD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Изменение динамической переменой или такт расчета case RDS_BFM_DYNVARCHANGE: case RDS_BFM_MODEL: // Запомненный указатель на структуру подписки Link=(RDS_PDYNVARLINK)BlockData->BlockData; // В s будут суммироваться элементы матрицы s=0; // Проверка существования динамической переменной if(Link!=NULL && Link->Data!=NULL) { // Копировать динамическую матрицу в Y rdsCopyVarArray(pY,Link->Data); // Если матрица не нулевого размера – суммировать if(RDS_ARRAYEXISTS(Link->Data)) { // Число элементов в матрице int count=RDS_ARRAYROWS(Link->Data)* RDS_ARRAYCOLS(Link->Data); // Указатель на начало данных матрицы double *data=(double*)RDS_ARRAYDATA(Link->Data); // Суммирование элементов в цикле for(int i=0;i<count;i++) s+=data[i]; } // Взвести Ready для передачи выходов по связям Ready=1; } break; } return RDS_BFR_DONE; // Отмена макроопределений #undef s #undef pY #undef Ready #undef Start #undef pStart } //=========================================
При инициализации блока эта модель подписывается на динамическую переменную «DynMatr», при очистке – прекращает подписку. Как и в предыдущей модели, указатель на структуру подписки запоминается в BlockData->BlockData. При запуске модели в режимах RDS_BFM_DYNVARCHANGE (изменение динамической переменной) и RDS_BFM_MODEL (такт расчета) матрица, находящаяся в динамической переменной, копируется в выход блока Y, после чего все ее элементы суммируются и выдаются на выход s. Для проверки, существует ли матрица (не нулевой ли у нее размер) и для получения числа строк, числа столбцов и указателя на первый элемент матрицы применяются те же самые макросы, которые использовались для статических переменных. Если переписать отвечающую за суммирование матрицы часть модели, таким образом, чтобы она вместо динамической переменной обращалась к статическому выходу блока Y, в который эта динамическая переменная была только что скопирована, текст программы будет выглядеть следующим образом (изменения выделены цветом):
// … // Если матрица не нулевого размера – суммировать if(RDS_ARRAYEXISTS(pY)) { // Число элементов в матрице int count=RDS_ARRAYROWS(pY)* RDS_ARRAYCOLS(pY); // Указатель на начало данных матрицы double *data=(double*)RDS_ARRAYDATA(pY); // Суммирование элементов в цикле for(int i=0;i<count;i++) s+=data[i]; } // …
В этом варианте указатель на область данных динамической переменной Link->Data заменен на указатель на статическую переменную pY, и модель полностью сохранила работоспособность, поскольку данные, расположенные по этим указателям, имеют одинаковую структуру.
Таким образом, если есть функция или макрос, выполняющая какое-либо действие над статической переменной, можно использовать ее и для выполнения этого действия над динамической, передав ей поле Data структуры подписки RDS_DYNVARLINK вместо указателя на статическую переменную. Это будет работать и для матриц, и для строк, и для структур, и для простых переменных.