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

Описание пользователя

Глава 3. Использование стандартных модулей автокомпиляции

§3.6. Принципы создания автокомпилируемых моделей блоков

§3.6.3. Работа с динамическими переменными

Описывается работа с динамическими переменными, то есть с переменными, которые модели блоков создают и уничтожают в процессе работы. Модели могут создавать такие переменные в корневой или родительской подсистеме блока, поэтому несколько блоков могут получить доступ к одной и той же динамической переменной и использовать ее для связи.

§3.6.3.1. Подключение к динамической переменной

Описывается получение данных из динамической переменной, созданной каким-либо другим блоком.

Принцип действия динамических переменных подробно рассматривается в §1.5 описания пользователя и еще подробнее – в §2.6 руководства программиста. Коротко напомним: динамическая переменная – это скрытая от пользователя переменная, общая для нескольких блоков, через которую они могут обмениваться информацией без использования связей. Какой-то один блок создает такую переменную, после чего он сам и другие блоки, зная имя этой переменной и ее тип, могут записывать и считывать ее значения. Блок, записавший в динамическую переменную новое значение, обычно информирует об этом остальные блоки, использующие эту переменную. При этом RDS вызывает у моделей этих блоков реакцию на специальное событие «изменение динамической переменной» (RDS_BFM_DYNVARCHANGE), в которой они считывают новое значение и используют его в расчете. Динамическая переменная обычно создается внутри корневой подсистемы схемы или родительской подсистемы создавшего ее блока (блок может создать динамическую переменную и внутри самого себя, но этот вариант используется крайне редко), при этом она доступна всем блокам, находящимся в одной с ней подсистеме или в подсистемах, вложенных в эту подсистему на любом уровне. Получение блоком доступа к динамической переменной называется в RDS подпиской на переменную: модель блока сообщает имя переменной, ее тип и подсистему, в которой ее нужно искать, а RDS связывает блок с этой переменной, если она существует. Если переменная не найдена, RDS запомнит факт подписки и предоставит блоку доступ к переменной, как только она появится. Таким образом, не важно, какое событие произойдет в схеме первым: создание динамической переменной одним блоком или подписка на нее другим: в конце концов связь блока с переменной будет установлена. Чаще всего блоки используют подписку с поиском по иерархии: при этом переменная с заданными именем и типом сначала ищется в родительской подсистеме подписывающегося блока, затем – в родительской подсистеме этой подсистемы и т.д. вплоть до корневой подсистемы. Этот механизм позволяет разместить блок, использующий переменную, в любой подсистеме, вложенной в ту, в которой находится блок-создатель этой переменной. При этом в разных подсистемах схемы могут находиться динамические переменные с одинаковыми именами, и блоки в этих подсистемах и подсистемах, вложенных в них, будут видеть ближайшую к ним переменную.

Среди стандартных блоков, работающих с динамическими переменными, самую важную роль играет блок-планировщик динамического расчета. Он создает в своей подсистеме вещественную (double) переменную «DynTime», в которую постоянно записывает значение условного системного времени (подробнее моделирование длящихся во времени процессов с использованием этой переменной описывается в §3.6.4). Планировщик изменяет это значение с шагом и скоростью, заданными в его настройках (рис. 389) – таким образом, можно настраивать дискретность изменения времени в системе и соотношение этого времени с реальным.

Блок-планировщик и его самые важные параметры

Рис. 389. Блок-планировщик и его самые важные параметры

Параметр «значение шага расчета» задает дискретность изменения системного времени, то есть размер интервала между соседними его отсчетами. Если, например, задать шаг расчета равным 0.1, динамическая переменная «DynTime» будет принимать значения 0, 0.1, 0.2, 0.3 и т.д. Увеличение значения времени на шаг расчета будет происходить не в каждом такте расчета, даже если включен флажок «новый шаг – каждый такт расчета» (использование флажка «по сигналу готовности» требует дополнительных соединений и здесь не рассматривается). Во-первых, если включен флажок «дополнительные такты», между изменениями «DynTime» будет выполняться по крайней мере указанное количество тактов расчета – это нужно для того, чтобы вычисленные после очередного изменения времени значения распространились по длинным цепочкам соединенных блоков. Во-вторых, если включен флажок «синхронизация с реальным временем», планировщик будет согласовывать изменение «DynTime» с системными часами и введенным в настройках множителем задержки. При множителе задержки, равном единице, системное время будет примерно совпадать с реальным: увеличив «DynTime» на заданное значение шага, планировщик будет ждать, когда по системным часам пройдет это же самое время. При множителе задержки, меньшем единицы, время в системе будет идти быстрее реального (при множителе 0.1 системное время идет в 10 раз быстрее), при множителе, большем единицы – медленнее (множитель 10 заставляет схему считать в 10 раз медленнее реального времени). Если флажок «синхронизация с реальным временем» выключен, схема будет считать с максимально возможной скоростью – это удобно, если нужно не наблюдать за протекающими в схеме процессами, а получить их графики или конечные значения как можно быстрее.

Флажок «завершить расчет по времени» позволяет автоматически остановить расчет при достижении переменной «DynTime», то есть системным временем, заданного значения. При этом, если включен дополнительный флажок «остановить процесс моделирования», RDS выйдет из режима расчета и вернется в режим моделирования. За редкими исключениями, связанными с проведением итеративных расчетов (например, с использованием стандартных блоков оптимизации), этот дополнительный флажок следует оставлять включенным.

Многие библиотечные блоки в RDS (графики, генераторы, динамические блоки) полагаются на наличие в схеме блока-планировщика. Точнее, им необходимо наличие динамической переменной «DynTime», из которой они берут текущее значение системного времени. Какой именно блок будет обеспечивать создание этой переменной и увеличение ее с некоторым шагом, этим блокам не важно. При желании, разработчик может написать собственную модель планировщика и использовать ее в схемах со стандартными блоками. При создании собственных моделей блоков, которым требуется значение системного времени, следует брать значение времени именно из переменной «DynTime» – так эти блоки смогут работать в одной схеме со стандартными динамическими и с блоком-планировщиком. В интерфейсе редактора модели можно достаточно просто добавлять связи блока с динамическими переменными. После создания такой связи в программе модели для этой переменной будет создаваться объект специального класса с переопределенными операторами присваивания и приведения типов. Таким образом, во вводимых в редакторе модели реакциях блока на события можно обращаться к динамической переменной по имени, получая ее значение и присваивая ей новое, а также вызывая функцию-член ее класса NotifySubscribers, уведомляющий все использующие ее блоки об изменении значения этой переменной (см. пример в §3.6.3.2).

В качестве простейшего примера блока, использующего значение системного времени, создадим модель генератора, выдающую на выход синусоиду по формуле A sin(w t), где t – системное время. У нашего блока будет два входа «A» и «w» (амплитуда и частота сигнала соответственно) и выход «y». Время мы будем брать из динамической переменной «DynTime», поэтому в структуре статических переменных блока переменная для времени не нужна. Структура переменных нашего блока будет такой:

Имя Тип Вход/выход Пуск Начальное значение
Start Сигнал Вход 0
Ready Сигнал Выход 0
A double Вход 0
w double Вход 0
y double Выход 0

Здесь мы не устанавливаем флажки «пуск» у добавленных входов блока, поскольку он должен запускаться при изменении времени, а не при изменении входов (флажок у входа запуска блока «Start» всегда включен, убрать его нельзя).

Создадим новый блок с автокомпилируемой моделью, зададим для него запуск по сигналу и введем в его модель указанную выше структуру переменных. Для того, чтобы связать блок с переменной времени «DynTime», следует выполнить следующие шаги:

Добавление переменной DynTime в блок

Рис. 390. Добавление переменной «DynTime» в блок

На вкладке «модель» в правой части окна редактора введем следующий текст:

  y=A*sin(w*DynTime);

Здесь мы в качестве значения времени подставили имя переменной DynTime. После всех этих действий, если все выполнено правильно, окно редактора модели должно иметь вид, изображенный на рис. 391.

Окно редактора модели генератора синусоиды

Рис. 391. Окно редактора модели генератора синусоиды

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

Любая модель блока, работающая с динамическими переменными, должна в обязательном порядке проверять существование этих переменных перед обращение к ним – таковы правила создания моделей в RDS. Кроме того, крайне желательно, чтобы модель реагировала на событие RDS_BFM_DYNVARCHANGE, наступающее при изменении любой динамической переменной, на которую подписан блок. Мы не включили в модель ни проверку существования «DynTime», ни реакцию на указанное событие, поскольку при параметрах модели по умолчанию (а мы их не меняли) блок автокомпиляции сделает это за нас. Вызовем из редактора окно параметров модели пунктом меню «модель | параметры модели» и рассмотрим те из них, которые связаны с динамическими переменными (рис. 392).

Параметры, обеспечивающие работу модели с динамическими переменными

Рис. 392. Параметры, обеспечивающие работу модели
с динамическими переменными

По умолчанию в модели установлен флажок «не реагировать на события без динамических переменных», поэтому введенная нами реакция на такт расчета выполнится только в том случае, если переменная «DynTime» будет существовать. Именно этот флажок позволяет нам не вставлять в программу проверку существования «DynTime», которая выглядела бы так:

  if(DynTime.Exists())
    y=A*sin(w*DynTime);

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

Флажок «автозапуск при изменении динамических переменных», тоже включенный по умолчанию, автоматически добавляет в модель реакцию на событие изменения динамической переменной, в которой сигналу запуска блока «Start» присваивается единица. Эта реакция не отображается в редакторе модели, но, при желании, ее можно увидеть, просмотрев полный текст формируемой программы пунктом меню «модель | показать текст C++» и найдя в нем метку «case RDS_BFM_DYNVARCHANGE». Таким образом, при изменении любой динамической переменной, на которую подписан блок, взведется его сигнал запуска, и в ближайшем такте расчета запустится его модель (в нашем случае выполнится оператор вычисления значения выхода блока «y»). Отключать этот флажок и вручную вводить в модель реакцию на событие RDS_BFM_DYNVARCHANGE имеет смысл либо если блок подписан на несколько переменных и необходимо знать, какая из них изменилась, либо при отсутствии у блока реакции на такт расчета, когда все действия выполняются в реакции на изменение динамической переменной. Пример использования этой реакции будет приведен в конце этого параграфа.

Вернемся к нашему блоку, в параметрах модели которого оба описанных выше флажка остались установленными по умолчанию. Для проверки его работы соберем схему, изображенную на рис. 393.

Тестирование генератора синусоиды

Рис. 393. Тестирование генератора синусоиды

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

Дважды щелкнув на планировщике, задав в его параметрах шаг расчета 0.1 и время остановки 10 секунд (синхронизацию с реальным временем можно включить или выключить по желанию) и запустив расчет, можно будет увидеть на графике изображение синусоиды с заданными в полях ввода амплитудой и частотой.

Модель нашего блока можно было бы построить и по-другому, выполняя все вычисления не в реакции на такт расчета, а в реакции на изменение динамической переменной. Сделаем это, и введем в модель ручную проверку существования переменной «DynTime», как было описано выше. Для этого выполним следующие действия:

  if(DynTime.Exists()) // DynTime есть
    y=A*sin(w*DynTime);
  else                 // DynTime нет
    y=;
  Ready=1;             // Выходные связи должны сработать

Теперь у нашей модели нет реакции на такт расчета. Вычисление значения выхода выполняется при изменении значения динамической переменной во введенной нами новой реакции: если переменная DynTime существует, будет вычислен синус, если нет, на выход будет подано значение переменной rdsbcppHugeDouble, указывающее на ошибку (это хуже, чем явное сообщение пользователю, но лучше, чем ничего). Затем мы присваиваем единицу сигналу готовности блока Ready, чтобы сработали связи, присоединенные к его выходу. Перед вызовом реакции на такт расчета RDS делает это автоматически, поэтому в предыдущем варианте модели мы не взводили Ready. Теперь мы работаем по изменению динамической переменной, и Ready автоматически не взводится – мы должны делать это вручную. Если убрать из программы последний оператор, выход блока будет вычисляться правильно (это можно увидеть, открыв окно его параметров и просмотрев текущие значения переменных после расчета), но на графике ничего не отобразится – вычисленное значение не поступит на график по связи.

Запустив расчет в схеме с новой моделью, мы увидим точно такой же график, что и со старой.

В созданной нами реакции на изменение динамических переменных мы не проверяем, какая именно переменная изменилась: наш блок подписан на единственную динамическую переменную «DynTime», поэтому в такой проверке нет необходимости. Если бы блок одновременно работал с несколькими переменными и выполнял с ними какие-либо сложные вычисления, имело бы смысл не выполнять действия, связанные с переменными, которые не изменились – это сэкономило бы процессорное время. Это сделать достаточно просто: реакция на изменение динамической переменной вызывается для каждой переменной, на которую подписан блок, при этом в ее параметрах передается используемый в RDS идентификатор изменившейся переменной. Этот идентификатор является параметром функции rdsbcppDynVarChange, внутрь которой вставляется введенный пользователем на вкладке «изменение динамической» текст программы. В верхней части этой вкладки, непосредственно над областью ввода текста, можно увидеть заголовок этой функции, выглядящий следующим образом:

  // Изменение динамической переменной
  void rdsbcppBlockClass::rdsbcppDynVarChange( Link)

Параметр Link типа RDS_PDYNVARLINK – это и есть тот самый идентификатор переменной, из-за изменения которой вызвана реакция. Идентификатор любой из динамических переменных блока можно получить функцией-членом GetLink, и, если сравнить результат этой функции с Link, можно узнать, не эта ли переменная изменилась. Оператор сравнения для переменной DynTime выглядел бы следующим образом:

  if(DynTime.GetLink()==Link)
    { // Действия при изменении DynTime
      …
    }

Можно также использовать логическую функцию-член CheckLink, которая возвращает истину, если переданный в ее параметре идентификатор совпадает с идентификатором ее объекта:

  if(DynTime.CheckLink(Link))
    { // Действия при изменении DynTime
      …
    }

Следует помнить, что параметр Link передается только в реакцию на изменение динамической переменной, поэтому в других реакциях (например, в реакции на такт расчета) приведенные выше проверки использовать нельзя. Если вычисления выполняются в реакции на такт расчета, необходимо вводить в переменные блока какие-либо дополнительные флаги, взводить их в реакции на изменение динамических переменных и проверять значения этих флагов в такте расчета.

Помимо упомянутых выше функций-членов NotifySubscribers, Exists, GetLink и CheckLink, у каждого объекта, создаваемого модулем автокомпиляции для динамической переменной, есть функции для создания, удаления, и получения доступа к переменной вручную. Они используются в тех случаях, когда нельзя жестко указать имя переменной в самой модели, как в рассмотренном примере. Эти функции и пример их использования описаны в §3.6.8.


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