Руководство программиста
Глава 2. Создание моделей блоков
§2.3. Структура функции модели блока
Рассматривается общая структура функции модели блока на языке C/C++, объясняется смысл ее параметров. Описывается структура данных, которую RDS хранит для каждого блока.
Любая функция модели блока RDS имеет следующий вид:
extern "C" __declspec(dllexport) int RDSCALL имя_функции( int CallMode, // Режим вызова RDS_PBLOCKDATA BlockData, // Данные блока LPVOID ExtParam) // Дополнительные параметры
Вызывая эту функцию, RDS передает ей три параметра:
- int CallMode – режим вызова. В этом параметре передается одна из констант RDS_BFM_*, описанных в файле «RdsDef.h». Каждой константе соответствует определенное событие, на которое может среагировать блок: переключение режима, такт расчета, нажатие кнопки мыши и т.п.
- RDS_PBLOCKDATA BlockData – указатель на структуру данных блока. В этой структуре содержится имя блока, адрес начала дерева (области памяти) статических переменных, адрес личной области данных блока и т.п.
- LPVOID ExtParam – дополнительные параметры, зависящие от конкретного значения CallMode, то есть режима вызова блока. Чаще всего это указатель на какую-либо структуру из описанных в «RdsDef.h» – например, при щелчке на изображении блока в этом параметре передается указатель на структуру RDS_MOUSEDATA (см. §2.12.1), в которой находятся координаты курсора мыши, текущие размеры блока и т.п.
Каждое значение режима вызова (параметра CallMode функции модели) соответствует определенному событию, на которое, при необходимости, может отреагировать модель блока. При этом для каждого события через параметр ExtParam передается указатель на данные (как правило, на структуру), описывающие произошедшее событие. Общее число событий, на которые может реагировать модель, довольно велико, их полный список приведен в приложении А. Среди них – инициализация и очистка данных блока, выполнение одного такта в режиме расчета, загрузка и сохранение параметров блока, реакция на мышь и клавиатуру, и т.п. Возвращаемое функцией значение интерпретируется RDS по-разному, в зависимости от цели вызова модели блока, то есть значения параметра CallMode. В большинстве случаев, возврат целой константы RDS_BFR_DONE, описанной в «RdsDef.h» и равной нулю, говорит об успешном завершении функции.
Структура данных блока RDS_BLOCKDATA устроена следующим образом:
typedef struct { LPVOID VarTreeData; // Начало дерева переменных LPVOID BlockData; // Указатель на личные данные RDS_BHANDLE Block; // Идентификатор блока RDSCSTR BlockNameA; // Имя блока (UTF8) RDSWCSTR BlockNameW; // Имя блока (UTF16) // RDSXCSTR BlockName; // Имя блока (поле-псевдоним, см. ниже) RDS_BHANDLE Parent; // Идентификатор подсистемы DWORD Flags; // Флаги RDSINT32 Width,Height; // Размеры блока RDSINT32 Tag; // Пользовательское поле } RDS_BLOCKDATA; typedef RDS_BLOCKDATA *RDS_PBLOCKDATA; // Указатель на структуру
- LPVOID VarTreeData – указатель на начало дерева статических переменных. Перед обращением к переменным модель должна убедиться, что их структура соответствует ее ожиданиям (для этого предусмотрен вызов модели с CallMode равным RDS_BFM_VARCHECK). Модель не должна изменять значение этого поля.
- LPVOID BlockData – указатель на личную область данных блока. Перед первым вызовом модели RDS присваивает этому полю значение NULL, после чего никогда к нему не обращается. Обычно блок, у которого есть личная область данных, отводит под нее память (например, оператором C++ new или функцией C malloc) при вызове модели с CallMode равным RDS_BFM_INIT, и присваивает указатель на отведенную область памяти полю BlockData. При этом при вызове модели с CallMode равным RDS_BFM_CLEANUP необходимо освободить отведенную память (например, оператором delete или функцией free). Пример модели, отводящей и освобождающей память под личную область данных, приведен в §2.4.
- RDS_BHANDLE Block – уникальный идентификатор данного блока. Такие идентификаторы используются во многих сервисных функциях для указания конкретного блока, с которым производится то или иное действие. Модель не должна изменять значение этого поля.
- RDSCSTR BlockNameA, RDSWCSTR BlockNameW – строки с именем блока в двух разных кодировках. Эти поля указывают на строки (завершенные нулевым кодом) имени блока в подсистеме. Строки находятся во внутренней памяти RDS, поэтому модель не должна изменять значение этих полей или менять какие-либо символы в этих строках – для переименования блока существует специальная сервисная функция rdsRenameBlock. Можно также пользоваться полем-псевдонимом BlockName, которое будет указывать на одну из этих строк в зависимости от наличия макроопределения RDS_UTF16DEFAULT.
- RDS_BHANDLE Parent – идентификатор родительской подсистемы блока. Для корневой подсистемы, у которой нет родительской, это поле равно NULL. Модель не должна изменять значение этого поля.
- DWORD Flags – битовые флаги, управляющие поведением блока. Некоторые примеры их использования будут приведены позже. Это поле может содержать любую комбинацию следующих флагов:
- RDS_VARCHECKFAILED – структура переменных блока не соответствует требованиям модели, то есть модель, вызванная с параметром RDS_BFM_VARCHECK, сообщила об ошибке. Модель может только читать этот флаг, его установка игнорируется RDS.
- RDS_NEEDSDLLREDRAW – изображение блока следует перерисовать при следующем обновлении окна (только для блоков, модели которых рисуют их самостоятельно). Этот флаг взводится RDS автоматически перед вызовом модели блока. Модель может сбросить его, если перерисовка не требуется, это позволяет избежать замедления работы RDS из-за излишне частого обновления окон.
- RDS_MOUSECAPTURE – блок захватил мышь. Модель может взвести или сбросить этот флаг только при реакции на перемещение мыши или нажатие и отпускание ее кнопок. Если модель взведет этот флаг, информация обо всех манипуляциях мышью в окне, в котором находится блок, будет поступать только в этот блок, независимо от того, над каким блоком находится курсор. После сброса этого флага восстановится нормальный порядок работы.
- RDS_NOWINREFRESH – перерисовка немодальных окон блока (если они есть) или окна подсистемы временно запрещена. Этот флаг используется для того, чтобы приостановить обновление окон на время какой-либо сложной операции, занимающей несколько тактов расчета. Например, при моделировании переходных процессов нежелательно обновлять окна между изменениями значения времени, когда часть блоков схемы еще не успела сработать. Обычно этот флаг устанавливается и сбрасывается сервисной функцией rdsEnableWindowRefresh, но модель может управлять им и самостоятельно.
- RDS_WINREFRESHWAITING – необходимо обновить немодальные окна блока или окно подсистемы, как только обновление будет разрешено. Этот флаг работает в паре с флагом RDS_NOWINREFRESH: если обновление окон запрещено и поступает команда обновления (по таймеру или от сервисной функции rdsRefreshBlockWindows), этот флаг взводится автоматически. Как только обновление окон снова будет разрешено сервисной функцией rdsEnableWindowRefresh, RDS проверит флаг RDS_WINREFRESHWAITING и, если он установлен, даст повторную команду на обновление окон.
- RDS_DISABLED – блок не реагирует на действия пользователя. Пользователь не может выделить, отредактировать или удалить этот блок. В режиме моделирования блок также не будет реагировать на мышь и клавиатуру. RDS никогда не устанавливает этот флаг, модель должна управлять им самостоятельно, если есть такая необходимость (по умолчанию он сброшен).
- RDS_CTRLCALC – перед началом каждого такта расчета модель этого блока должна быть вызвана в режиме RDS_BFM_PREMODEL, если для блока установлен запуск каждый такт или его первая однобайтовая переменная («Start») имеет значение 1. По умолчанию этот флаг сброшен, обычно его взводят модели управляющих блоков, которым необходимо выполнить какие-либо действия до того, как начнется очередной такт расчета.
- int Width,Height – ширина и высота изображения блока в точках экрана при масштабе 100%. Значения этих полей используются только тогда, когда модель самостоятельно рисует внешний вид блока в подсистеме. Если внешний вид блока определяется векторной картинкой или прямоугольником с текстом, эти поля не используются.
- int Tag – целое поле, которое разработчик модели может использовать по своему усмотрению, например, для хранения каких-либо меток или флагов. RDS его не инициализирует и не обрабатывает.
Далее будут рассмотрены примеры моделей, в которых реализованы реакции на различные события.