Навигация:
<< >> Оглавление Указатель
Текст С++

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

Глава 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  TestDynSinT(int CallMode,
                                BlockData,
                                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)))
    // Вспомогательная переменная – указатель на структуру подписки
     Link;

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

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

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

        // Выполнение такта моделирования
        case :
          // Запомненный указатель на структуру подписки
          Link=()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 :
          Start=1; // Запустить модель в следующем такте расчета
          break;
      }
    return ;
  // Отмена макроопределений
  #undef y
  #undef A
  #undef Ready
  #undef Start
  #undef pStart
  }
  //=========================================

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

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

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

При вызове модели с параметром 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», нужно каким-либо образом отслеживать эти изменения. Поскольку эта переменная формируется стандартным блоком-планировщиком, про который точно известно, что он уведомляет всех подписчиков о ее изменении, в модель можно ввести реакцию на событие . При вызове функции модели в этом режиме стандартному сигналу Start будет присвоено значение 1, что приведет к запуску модели в следующем такте расчета. Тогда и будет вычислено новое значение y.

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

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

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


<< >> Оглавление Указатель