Описание пользователя
Глава 3. Использование стандартных модулей автокомпиляции
§3.6. Принципы создания автокомпилируемых моделей блоков
§3.6.8. Программное управление динамическими переменными
Описывается программное выполнение всех действий по созданию, удалению и подписке на динамические переменные.
В §3.6.7 рассматриваются модели блоков, в которых имена динамических переменных хранятся в настроечных параметрах блоков. Модуль автокомпиляции при этом автоматически добавляет в программу модели вызовы, необходимые для удаления и прекращения подписки на переменную со старым именем и создания и подписки на переменную с новым именем при изменении этого имени. В некоторых, довольно редких, случаях может потребоваться задавать имя динамической переменной программно – например, при изменении состояния блока или значений других его переменных. Это осуществимо, но при этом придется отказаться от автоматического создания переменной и автоматического получения к ней доступа – эти функции, добавляемые в модель модулем автокомпиляции, нужно будет отключить. Вместо этого придется создавать переменную и получать к ней доступ вручную при помощи специальных функций-членов объекта для работы с переменной. В каждом таком объекте для этой цели предусмотрено четыре функции:
- BOOL Create(int Block,char *Name,BOOL Fixed)
- Создание динамической переменной с именем Name в блоке, указываемом целым параметром Block, который может принимать одно из трех значений: RDS_DVSELF (создать переменную в вызвавшем блоке), RDS_DVPARENT (создать переменную в родительской подсистеме) или RDS_DVROOT (создать переменную в корневой подсистеме). Чаще всего используются варианты RDS_DVPARENT и RDS_DVROOT, поскольку переменная, созданная в простом блоке, не будет видна никому, кроме этого блока. Значение TRUE в параметре Fixed разрешает удалять переменную только создавшему ее блоку, значение FALSE – любому из блоков схемы. Функция возвращает успешность создания переменной. Если вызвать эту функцию-член у объекта, который уже связан с созданной динамической переменной, ранее созданная переменная будет удалена, после чего будет создана новая, согласно параметрам функции.
- BOOL Delete(void)
- Удалить переменную, ранее созданную вызовом Create. Возвращается успешность удаления.
- BOOL Subscribe(int Block,char *Name,BOOL Search)
- Получить доступ к переменной с именем Name в блоке, указываемом целым параметром Block, который принимает те же три значения, что и одноименный параметр функции Create: RDS_DVSELF (искать переменную в вызвавшем блоке), RDS_DVPARENT (искать переменную в родительской подсистеме) или RDS_DVROOT (искать переменную в корневой подсистеме). Значение TRUE в параметре Search заставит RDS искать переменную с указанным в параметре Name именем и заложенным в сам объект типом начиная с указанного блока вверх по всей иерархии подсистем вплоть до корневой. Функция возвращает FALSE только в случае какой-либо серьезной ошибки – например, если вместо имени переменной передана пустая строка. TRUE возвращается даже тогда, когда переменная с указанным именем не найдена: RDS запоминает факт обращения к этой переменной и, как только она появится, предоставит блоку доступ к ней. Для проверки фактического существования переменной используется функция-член Exists. Если вызвать функцию Subscribe у объекта, который уже связан с какой-либо динамической переменной, прежняя связь будет разорвана и будет установлена новая.
- BOOL Unsubscribe(void)
- Разорвать связь с динамической переменной, ранее установленную вызовом Subscribe.
Во всех этих функциях тип динамической переменной не указывается, он всегда жестко закладывается в объект для работы с ней при его добавлении в редактор модели.
В качестве примера использования этих функций изменим пример из §3.6.7: будем вручную выполнять все действия, которые необходимы для изменения имени переменной. Начнем с модели блока-передатчика.
Прежде всего откроем редактор модели блока, дважды щелкнем на динамической переменной «DynVar», в окне ее параметров в выпадающем списке «» выберем вариант «все действия – вручную» (рис. 438) и закроем окно кнопкой «».
Рис. 438. Параметры объекта для динамической переменной
без привязки к конкретной переменной
Рис. 439. Измененный объект в списке
динамических переменных модели
После закрытия окна в списке динамических переменных редактора модели в левой колонке рядом с именем объекта «DynVar» будет нарисован пустой белый квадрат (рис. 439) – это указывает на то, что объект автоматически не связывается с какой-либо динамической переменной.
Теперь в нашей модели есть объект DynVar для обращения к динамической переменной типа double, не связанный автоматически ни с какой переменной, и объект VarName, хранящий имя переменной, введенное пользователем в настройках (он остался от прежней версии модели). Необходимо дать указание объекту DynVar создать переменную с таким именем – для этого служит его функция-член Create, описанная выше. Причем вызывать эту функцию нужно после любого изменения значения настроечного параметра VarName.
В автокомпилируемой модели есть всего два места, в которых значение настроечных параметров может меняться. Во-первых, их значения могут быть изменены пользователем – это происходит в момент закрытия окна настройки блока кнопкой «». Во-вторых, значения параметров изменяются в момент загрузки данных блока – это происходит при загрузке в память схемы с этим блоком, при отмене пользователем сделанного им изменения в схеме, при вставке блока в схему из библиотеки или буфера обмена, и т.п. Нам нужно создавать динамическую переменную с новым именем вызовом функции Create в обоих этих случаях. Эта функция автоматически удаляет переменную, которая была ранее создана этим же объектом, поэтому при изменении имени переменной в параметре нам не придется заботиться об удалении переменной со старым именем перед созданием новой.
Начнем с создания переменной при изменении пользователем ее имени в окне настроек блока. Если в модель добавлено окно настроек, при его закрытии кнопкой «» вызывается реакция на событие «вызов настройки». Добавим в нашу модель эту реакцию: на левой панели окна редактора выберем вкладку «», раскроем на ней раздел «» и дважды щелкнем на его подразделе «». При этом значок подраздела станет желтым, а в правой части окна появится новая пустая вкладка «». Введем на ней следующий текст:
DynVar.Create(RDS_DVPARENT,VarName.c_str(),TRUE);
Здесь мы вызываем у объекта DynVar функцию-член Create, передавая ей параметры RDS_DVPARENT (создать переменную в родительской подсистеме блока), VarName.c_str() (строка с именем переменной из настроечного параметра VarName) и TRUE (удалить созданную переменную сможет только этот блок). Переменную мы создаем в родительской подсистеме блока-передатчика, чтобы к ней могли получить доступ блоки в одной с ним подсистеме и в подсистемах, вложенных в нее. Следует обратить внимание на то, что во втором параметре функции мы вызываем у параметра VarName функцию-член c_str: дело в том, что VarName имеет тип rdsbcppString (это объект специального класса для хранения строк), а функция Create требует имени переменной типа char*. Функция c_str возвращает указатель на строку char*, хранящуюся внутри объекта.
Теперь, если пользователь откроет окно настроек блока и закроет его кнопкой «», мы стираем ранее созданную блоком динамическую переменную, и создаем новую, с именем, взятым из параметра VarName. Все это делает вызов функции Create.
Добавим в нашу модель реакцию на загрузку параметров блока. На вкладке «» раскроем раздел «» (важно не перепутать его с похожим разделом «») и дважды щелкнем на его подразделе «». Значок подраздела станет желтым, а в правой части окна появится новая вкладка «», на которой нужно ввести точно такой же вызов функции Create, какой мы ввели на вкладке «» (можно скопировать его оттуда через буфер обмена). Теперь при загрузке данных блока, когда сохраненное ранее значение параметра VarName будет восстановлено в момент загрузки отдельного блока или всей схемы, мы тоже создаем динамическую переменную с новым именем.
Уничтожение созданных динамических переменных при уничтожении данных всего блока (например, при закрытии RDS или перед загрузкой в память другой схемы) производится автоматически, но, чтобы проиллюстрировать возможность программного удаления переменной, сделаем это вручную, в реакции на событие очистки данных блока. На вкладке «» раскроем раздел «» и дважды щелкнем на его подразделе «». Введем на открывшейся вкладке следующий текст:
DynVar.Delete();
Здесь мы просто удаляем ранее созданную переменную – у функции Delete нет параметров.
Осталось изменить реакцию на такт расчета, в которой значение входа блока копируется в динамическую переменную. Раскроем раздел «» вкладки «» и дважды щелкнем на его подразделе «». На открывшейся одноименной вкладке введем следующий текст:
if(DynVar.Exists()) { DynVar=x; DynVar.NotifySubscribers(); }
В отличие от модели из §3.6.7, здесь мы обязательно должны убедиться в существовании динамической переменной, поскольку при создании объекта DynVar мы указали, что все действия с ним мы будем выполнять вручную. Если функция-член Exists этого объекта вернет TRUE, значит, переменная существует, и мы можем присвоить ей значение с входа блока x и уведомить об этом всех ее подписчиков вызовом NotifySubscribers.
Модель передатчика готова – отредактируем теперь модель приемника. В ней нужно изменить параметры объекта «DynVar» точно так же, как они были изменены для передатчика: в выпадающем списке «» следует выбрать вариант «все действия – вручную» (см. рис. 438).
Добавим в модель команды для получения доступа к переменной, имя которой содержится в параметре VarName. Как и в предыдущей модели, их нужно выполнять при изменении значения настроечного параметра, то есть в реакциях на вызов окна настроек (вкладка «», раздел «», подраздел «») и на загрузку данных блока (раздел «», подраздел «»). В обеих реакциях введем один и тот же текст:
DynVar.Subscribe(RDS_DVPARENT,VarName.c_str(),TRUE);
У объекта DynVar мы вызываем функцию-член Subscribe, передавая ей параметры RDS_DVPARENT (искать переменную в родительской подсистеме блока), VarName.c_str() (строка с именем переменной из настроечного параметра VarName) и TRUE (если переменной не будет в родительской подсистеме, искать ее далее вверх по иерархии). Эта функция автоматически разорвет старую связь объекта DynVar с переменной, если она была установлена ранее, и даст ему доступ к переменной с новым именем.
При уничтожении данных всего блока разрыв связей с динамическими переменными, доступ к которым он затребовал, производится автоматически, поэтому мы могли бы не предпринимать для этого никаких дополнительных действий. Чтобы проиллюстрировать возможность программного разрыва связи с переменной, все-таки сделаем это вручную в реакции на событие очистки данных блока (раздел «», подраздел «» на вкладке «»). Введем в эту реакцию такой текст:
DynVar.Unsubscribe();
Функция Unsubscribe без параметров разрывает ранее созданную функцией Subscribe связь с динамической переменной.
Теперь отредактируем в модели реакцию на изменение динамической переменной (раздел «», подраздел «»), в которой мы считываем число из этой переменной и выдаем его на выход. Новый текст реакции будет таким:
if(DynVar.Exists()) { y=DynVar; Ready=1; }
Прежде чем обращаться к динамической переменной через объект DynVar, мы должны проверить, существует ли эта переменная. Даже если в параметрах модели включен запрет ее вызова при отсутствии динамических переменных, на объект DynVar этот запрет не распространяется – при создании объекта мы указали, что все действия с ним мы будем выполнять вручную. Поэтому сначала мы вызываем его функцию-член Exists. Только если она вернет TRUE, что будет означать существование переменной, мы скопируем ее значение в выход блока y и взведем сигнал готовности Ready (в отличие от реакции на такт расчета, в реакции на изменение динамической переменной сигнал готовности не взводится автоматически).
Измененные модели приемника и передатчика будут работать точно так же, как и модели, описанные в §3.6.7, пользователь не заметит между ними разницы. В данном случае для решения поставленной задачи вызов функций для программного управления переменными не требуется, они используются только для демонстрации работы с ними.