Руководство программиста
Глава 2. Создание моделей блоков
§2.5. Статические переменные блоков
§2.5.8. Использование выходов с управляющими переменными
Описывается использование управляющих переменных, которые позволяют запретить передачу по связям значения конкретного выхода блока, а также активировать связь, подключенную к конкретному элементу выхода-массива. Приведен пример модели переключателя с двумя выходами, передающего входное значение на один или на оба выхода в зависимости от значения дополнительного входа (работа неактивного выхода блокируется управляющей переменной). Также приведен пример демультиплексора на произвольное число выходов (активный выход выбирается целой управляющей переменной массива).
Связи, подключенные к выходам простого блока, срабатывают в режиме расчета только тогда, когда значение стандартного сигнала «Ready» (сигнального выхода блока со смещением 1) равно единице. Перед запуском модели блока в режиме RDS_BFM_MODEL RDS автоматически взводит этот сигнал, поэтому, если модель не предпримет никаких действий, в конце такта расчета значения всех выходов блока будут переданы на входы других блоков, связанных с ним. Если модель присвоит переменной «Ready» нулевое значение, ни одна из связей, соединенных с выходами блока, не сработает. Чаще всего модели сбрасывают «Ready», если значения выходов блока не изменились, поскольку в этом случае нет никакого смысла передавать по связям те же самые данные еще раз. Повторная передача данных не приведет к возникновению каких-либо ошибок, но из-за срабатывания связей могут запуститься модели блоков, ко входам которых эти связи подключены. Поскольку входные данные этих моделей не изменились, их запуск был бы напрасной тратой времени.
Разрешение и запрещение передачи данных отдельных выходов по связям часто требуется при создании блоков-выключателей и демультиплексоров, управляющих передачей данных со входа на один или несколько выходов. Использование сигнала «Ready» позволяет управлять только всеми выходами одновременно: или сработают все связи, или не сработает ни одна из них. Для переключателей с единственным выходом (см. пример в §2.5.6) это вполне подходит, однако, если выходов несколько, бывает необходимо управлять ими независимо – разрешать передачу данных для одних выходов, запрещая при этом передачу других.
Для управления передачей данных конкретного выхода блока необходимо ввести дополнительную логическую переменную и связать с ней выход, задав для него тип «выход/логическая» вместо «выход» и указав имя логической переменной (см. рис. 9). При этом связи, подключенные к этому выходу, будут передавать данные только в том случае, если связанная логическая переменная будет иметь значение 1. Разумеется, сигнал «Ready» также должен быть равен единице, иначе ни одна выходная связь блока не сработает, какие бы значения не имели управляющие логические переменные выходов. Если выход блока – массив, можно вместо логической управляющей переменной указать для него целую. Целая переменная будет управлять только связями, подключенными к отдельным элементам массива, никак не влияя на связи, подключенные ко всему массиву как к одной сложной переменной. Значение переменной будет определять номер элемента массива, для которого разрешена передача данных. Если переменная будет иметь значение 0, будут работать только связи, присоединенные к нулевому элементу массива, если она будет равна единице – только связи, присоединенные к первому элементу и т.д.
Рис. 35. Управление отдельными
выходными связями блока
Для примера сначала рассмотрим блок, который должен передавать данные вещественного входа «x» на один из выходов «y0» и «y1» в зависимости от значения переменной «N»: при нулевом «N» значение должно передаваться на выход «y0», при «N»=1 – на выход «y1», при «N»=2 – на оба выхода (рис. 35). Для этого блока потребуется раздельное управление выходами, поэтому придется ввести в него две дополнительных логических переменных «L0» и «L1», которые будут управлять выходами «y0» и «y1» соответственно:
| Смещение | Имя | Тип | Размер | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|---|---|
| 0 | Start | Сигнал | 1 | Вход | ✓ | 1 |
| 1 | Ready | Сигнал | 1 | Выход | 0 | |
| 2 | x | double | 8 | Вход | ✓ | 0 |
| 10 | N | int | 4 | Вход | ✓ | 0 |
| 14 | L0 | Логическая | 1 | Внутренняя | 0 | |
| 15 | L1 | Логическая | 1 | Внутренняя | 0 | |
| 16 | y0 | double | 8 | Выход/логическая L0 |
0 | |
| 24 | y1 | double | 8 | Выход/логическая L1 |
0 |
Модель блока будет выглядеть так:
extern "C" __declspec(dllexport) int RDSCALL TestSW2(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define x (*((double *)(pStart+2*RDS_VSZ_S))) #define N (*((RDSINT32 *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D))) #define L0 (*((char *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D+RDS_VSZ_I))) #define L1 (*((char *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D+RDS_VSZ_I+ \ RDS_VSZ_L))) #define y0 (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D+RDS_VSZ_I+ \ 2*RDS_VSZ_L))) #define y1 (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D+ \ RDS_VSZ_I+2*RDS_VSZ_L))) switch(CallMode) { // Проверка типа переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSDILLDD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Выполнение такта моделирования case RDS_BFM_MODEL: switch(N) { case 0: // Передать данные на выход y0 y0=x; L0=1; // Разрешить y0 L1=0; // Запретить y1 break; case 1: // Передать данные на выход y1 y1=x; L0=0; // Запретить y0 L1=1; // Разрешить y1 break; default: // Передать данные на оба выхода y0=y1=x; L0=L1=1; // Разрешить оба выхода } break; } return RDS_BFR_DONE; // Отмена макроопределений #undef y1 #undef y0 #undef L1 #undef L0 #undef N #undef x #undef Ready #undef Start #undef pStart } //=========================================
В режиме RDS_BFM_MODEL модель выполняет разные действия в зависимости от значения входа N. Если значение N равно нулю, выходу y0 присваивается значение входа x, после чего логической переменной L0 присваивается единица, а L1 – ноль. При таких значениях управляющих переменных сработает только связь, присоединенная к выходу y0, которым управляет L0. Если значение N равно единице, все происходит наоборот – переменной L0 присваивается 0, а L1 – единица, при этом будет работать только связь, присоединенная к y1. Если же N имеет какое-либо другое значение, обеим управляющим переменным присваивается значение 1, что разрешает работу связей, присоединенных к обоим выходам блока.
Рис. 36. Управление передачей
отдельных элементов массива
Теперь рассмотрим пример, в котором целая переменная будет управлять передачей данных элементов массива. Предположим, что необходимо создать модель блока-демультиплексора, который будет передавать значение вещественного входа «x» на выход, номер которого определяется целым входом «N». Поскольку заранее неизвестно, сколько выходов должно быть у блока, его выходом будет вещественный массив «Y», к элементам которого будут подключаться связи (рис. 36). Чтобы для любого значения «N» срабатывала только связь, соединенная с элементом «Y[N]», целая переменная «N» будет указана в качестве управляющей для массива «Y»:
| Смещение | Имя | Тип | Размер | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|---|---|
| 0 | Start | Сигнал | 1 | Вход | ✓ | 1 |
| 1 | Ready | Сигнал | 1 | Выход | 0 | |
| 2 | x | double | 8 | Вход | ✓ | 0 |
| 10 | N | int | 4 | Вход | ✓ | 0 |
| 14 | Y | Массив double | 16 | Выход/логическая N |
В данном случае управляющая массивом «Y» переменная «N» одновременно является входом блока, поэтому в модели не нужно отдельно управлять номером выхода, связь которого будет работать – это произойдет автоматически при поступлении на вход «N» нового значения. Модель блока будет выглядеть следующим образом:
extern "C" __declspec(dllexport) int RDSCALL TestSW(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { // Макроопределения для статических переменных #define pStart ((char *)(BlockData->VarTreeData)) #define Start (*((char *)(pStart))) #define Ready (*((char *)(pStart+RDS_VSZ_S))) #define x (*((double *)(pStart+2*RDS_VSZ_S))) #define N (*((RDSINT32 *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D))) #define pY ((void **)(pStart+2*RDS_VSZ_S+RDS_VSZ_D+RDS_VSZ_I)) switch(CallMode) { // Проверка типа переменных case RDS_BFM_VARCHECK: if(strcmp((char*)ExtParam,"{SSDIMD}")==0) return RDS_BFR_DONE; return RDS_BFR_BADVARSMSG; // Выполнение такта моделирования case RDS_BFM_MODEL: if(N<0) // Значение N не должно быть отрицательным Ready=0; // Не передавать ничего по связям else // Значение N не отрицательно { int count; double *array; // Число элементов в массиве Y count=RDS_ARRAYEXISTS(pY)?RDS_ARRAYCOLS(pY):0; if(N>=count) // Число элементов недостаточно { // Увеличение размера Y if(!rdsResizeVarArray(pY,1,N+1,TRUE,NULL)) { // Ошибка: не удалось увеличить размер массива rdsStopCalc(); // Остановка расчета // Не передавать ничего по связям Ready=0; // Вывод сообщения rdsMessageBoxW(L"Мало памяти", L"Ошибка", MB_OK | MB_ICONERROR); return RDS_BFR_DONE; } } // Получить указатель на первый элемент Y array=(double*)RDS_ARRAYDATA(pY); // Записать в Y[N] значение входа array[N]=x; } // else (N>=0) break; } return RDS_BFR_DONE; // Отмена макроопределений #undef pY #undef N #undef x #undef Ready #undef Start #undef pStart } //=========================================
Поскольку у массива не может быть элементов с отрицательными индексами, модель в режиме RDS_BFM_MODEL сначала проверяет неотрицательность значения N. Если N отрицательно, сигнал Ready обнуляется и работа модели на этом завершается. В противном случае текущее число элементов массива Y записывается во вспомогательную переменную count и сравнивается с N (поскольку число строк массива всегда равно единице, число элементов в нем всегда равно числу столбцов и вычисляется при помощи макроса RDS_ARRAYCOLS). Если значение N больше или равно числу элементов в Y, значит, элемент Y[N] еще не существует, и размер массива нужно увеличить до N+1. Для этого вызывается сервисная функция rdsResizeVarArray и проверяется возвращаемое ей значение. Если размер массива увеличить не удалось (из-за нехватки памяти или слишком большого значения N), функция вернет FALSE. В этом случае модель остановит расчет, вызвав функцию rdsStopCalc, присвоит сигналу Ready значение 0 и выведет сообщение «Мало памяти» при помощи функции rdsMessageBox. Обнуление сигнала Ready может показаться лишним, поскольку расчет все равно будет остановлен, однако остановка расчета произойдет только после завершения текущего такта моделирования. Если не обнулить Ready, значение выхода Y будет передано по связям в конце такта, что нежелательно.
После того, как модель удостоверилась в существовании элемента массива Y[N] (или создала его, увеличив размер Y), этому элементу присваивается значение входа x. Поскольку переменная N указана как управляющая для выхода Y, только это значение будет передано по связям в конце такта расчета.