Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.6. Динамические переменные

§2.6.2. Подписка на динамическую переменную

# Руководство программиста

## Глава 2. Создание моделей блоков

### §2.6. Динамические переменные

#### §2.6.2. Подписка на динамическую переменную

Приводится пример модели блока, получающего доступ к стандартной динамической переменной времени «DynTime», создаваемой блоком-планировщиком, и используемой большинством библиотечных блоков. Блок в примере вычисляет синус от времени, умножает его на значение входа и выдает на выход. В примере также рассматривается реакция модели на событие RDS_BFM_DYNVARCHANGE, сигнализирующее о том, что динамическая переменная изменилась.

Рассмотрим пример, в котором модель блока получит доступ к динамической переменной, созданной другим блоком. Создадим блок, вычисляющий значение «y=A×sin(T)», где «A» – вход блока, «y» – выход, а «T» – значение текущего времени из динамической переменной «DynTime» типа double, которая создается и изменяется планировщиком динамического расчета. Блок будет иметь следующую структуру переменных:

Смещение | Имя | Тип | Размер | Вход/выход | Пуск | Начальное значение
--- | --- | --- | --- | --- | --- | ---
0 | Start | Сигнал | 1 | Вход | ✓ | 1
1 | Ready | Сигнал | 1 | Выход |  | 0
2 | A | double | 8 | Вход | ✓ | 0
10 | y | double | 8 | Выход |  | 0

В параметрах блока следует отключают флаг «запуск каждый такт» и установить флаг «пуск» для входа «A» – теперь блок будет автоматически запускаться при срабатывании связи, подключенной к этому входу. Также желательно дать сигналу «Start» начальное значение 1, чтобы блок запустился в самом первом такте расчета, вычислив значение выхода «y» по начальному значению «A». Чтобы блок запускался и при изменении переменной «DynTime», необходимо будет ввести в его модель реакцию на событие RDS_BFM_DYNVARCHANGE. Модель блока будет иметь следующий вид:

      extern "C" __declspec(dllexport)
           int RDSCALL TestDynSinT(int CallMode,
                                   RDS_PBLOCKDATA BlockData,
                                   LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart  ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define A (*((double *)(pStart+2*RDS_VSZ_S)))
      #define y (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
        // Вспомогательная переменная – указатель на структуру подписки
        RDS_PDYNVARLINK Link;

        switch(CallMode)
          { // Инициализация блока
            case RDS_BFM_INIT:
              // Подписка на динамическую переменную DynTime
              Link=rdsSubscribeToDynamicVar(RDS_DVPARENT, // В родителе
                                            "DynTime",    // Переменная
                                            "D",          // Тип
                                            TRUE);        // Искать
              // Запомнить указатель на структуру подписки
              BlockData->BlockData=Link;
              break;

            // Очистка
            case RDS_BFM_CLEANUP:
              // Запомненный указатель на структуру подписки
              Link=(RDS_PDYNVARLINK)BlockData->BlockData;
              // Прекратить подписку на DynTime
              rdsUnsubscribeFromDynamicVar(Link);
              break;

            // Проверка типа статических переменных
            case RDS_BFM_VARCHECK:
              if(strcmp((char*)ExtParam,"{SSDD}")==0)
                return RDS_BFR_DONE;
              return RDS_BFR_BADVARSMSG;

            // Выполнение такта моделирования
            case RDS_BFM_MODEL:
              // Запомненный указатель на структуру подписки
              Link=(RDS_PDYNVARLINK)BlockData->BlockData;
              // Проверка существования DynTime
              if(Link!=NULL && Link->Data!=NULL)
                { // DynTime существует – привести указатель на область
                  // данных переменной к типу "double*"
                  double *pT=(double*)Link->Data;
                  // pT – указатель на данные DynTime
                  y=A*sin(*pT); // Вычисление выхода
                }
              else // DynTime не существует
                Ready=0; // Не передавать данные по связям
              break;

            // Реакция на изменение динамической переменной
            case RDS_BFM_DYNVARCHANGE:
              Start=1; // Запустить модель в следующем такте расчета
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef y
      #undef A
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================

При инициализации блока (вызове модели с параметром RDS_BFM_INIT) модель вызывает функцию rdsSubscribeToDynamicVar, запрашивая у RDS подписку на переменную «DynTime» типа double (строка «D») с поиском по иерархии (параметр функции Search равен TRUE) в родительской подсистеме (константа RDS_DVPARENT). Таким образом, если где-нибудь между родительской подсистемой этого блока и корневой подсистемой будет находиться планировщик динамического расчета, эта модель получит доступ к созданной им переменной «DynTime». Возвращаемый функцией указатель на созданную структуру подписки присваивается вспомогательной переменной Link. Для того, чтобы динамическую переменную можно было использовать в модели блока при реакции на другие события (например, при выполнении такта моделирования), необходимо как-нибудь запомнить этот указатель, поскольку вспомогательная переменная Link, как и любая автоматическая переменная в языке C, будет уничтожена при завершении функции модели и ее значение будет потеряно. У некоторых программистов может возникнуть соблазн объявить переменную Link статической, чтобы ее значение сохранялось между вызовами функции модели, но этого ни в коем случае нельзя делать. В RDS одна и та же функция вызывается для всех блоков с данной моделью, сколько бы их ни было, при этом в параметре BlockData передаются данные конкретного блока, для которого вызвана функция. Если объявить статической какую-либо переменную внутри функции модели, она станет общей для всех блоков с этой моделью. В данном случае это приведет к тому, что блоки будут обращаться не к тем динамическим переменным, на которые они подписались, а к переменной, на которую подписался последний из них, поскольку он присвоил общей переменной Link новое значение. При удалении любого из этих блоков структура подписки будет уничтожена, и все остальные блоки, пытаясь получить доступ к динамической переменной, обратятся к уже освобожденной области памяти, что с большой вероятностью вызовет ошибку общей защиты.

Для хранения данных, относящихся к конкретному блоку, в том числе и указателей на структуры подписки, чаще всего используется личная область данных блока. Обычно при инициализации блока модель отводит необходимую для личной области данных память и присваивает указатель на эту область полю BlockData структуры данных блока (пример модели, работающей с личной областью данных, приведен в §2.4). При написании модели на языке C/C++ личная область данных блока обычно оформляется как объект какого-либо класса или структуры, и указатели на структуры подписки делают членами этого класса. В рассматриваемом примере модели необходимо хранить для каждого блока только один параметр – указатель на структуру подписки на переменную «DynTime». Можно было бы описать структуру личной области данных с единственным полем типа RDS_PDYNVARLINK (указатель на структуру подписки), отвести память под эту структуру при инициализации блока и присвоить этому полю значение вспомогательной переменной Link, но в данном случае можно поступить проще – будем считать личной областью данных блока ту самую структуру подписки, указатель на которую находится в переменной Link. Отведением и освобождением памяти под эту структуру занимается RDS, поэтому никаких дополнительных действий в функции модели не потребуется – нужно просто присвоить значение переменной Link переменной BlockData->BlockData, предназначенной для хранения указателя на личную область данных блока, что и делается в последнем операторе реакции модели на событие RDS_BFM_INIT.

При вызове модели в режиме RDS_BFM_CLEANUP (при отключением модели от блока, перед уничтожением блока и т.п.), блок прекращает подписку на переменную «DynTime». Для этого вызывается функция rdsUnsubscribeFromDynamicVar, в которую передается указатель на структуру подписки. Поскольку этот указатель был сохранен в поле структуры данных блока BlockData->BlockData, которая имеет тип void* (произвольный указатель), перед вызовом функции он приводится к типу RDS_PDYNVARLINK.

При вызове модели с параметром RDS_BFM_MODEL вспомогательной переменной Link присваивается (с приведением типа) запомненный указатель на структуру подписки, хранящийся в поле BlockData структуры данных блока. Затем модель проверяет, удалось ли ей получить доступ к запрошенной переменной. Если RDS по каким-либо причинам не удалось выполнить подписку и создать соответствующую структуру (например, при нехватке памяти), значение переменной Link будет равно NULL. Если переменной «DynTime» нет ни в одной из подсистем, просмотренных в процессе поиска, значение поля Data структуры подписки (Link->Data) будет равно NULL. Если же и Link, и Link->Data имеют ненулевые значения, значит, RDS удалось найти переменную «DynTime» и записать указатель на ее область данных в поле Link->Data. В этом случае указатель на область данных переменной приводится к типу double* (указатель на double) и присваивается вспомогательной переменной pT, после чего с использованием этой переменной вычисляется значение выхода y.

Для того, чтобы значение выхода y вычислялось заново при каждом изменении значения «DynTime», нужно каким-либо образом отслеживать эти изменения. Поскольку эта переменная формируется стандартным блоком-планировщиком, про который точно известно, что он уведомляет всех подписчиков о ее изменении, в модель можно ввести реакцию на событие RDS_BFM_DYNVARCHANGE. При вызове функции модели в этом режиме стандартному сигналу Start будет присвоено значение 1, что приведет к запуску модели в следующем такте расчета. Тогда и будет вычислено новое значение y.

Рис. 40. Доступ к динамической переменной времени

Чтобы проверить работу блока с этой моделью, следует поместить его в схему (см. рис. 40) вместе со стандартным блоком-планировщиком (на рисунке он располагается в левом верхнем углу) и подключить ко входу блока «A» поле ввода, а к выходу «y» – график, получающий значение времени из той же самой переменной «DynTime» (это задается в параметрах графика, впрочем, по умолчанию стандартные графики работают именно с этой переменной). При запуске расчета график должен отобразить синусоиду с амплитудой, равной значению в поле ввода, подключенном ко входу «A».