Описание пользователя
Глава 3. Использование стандартных модулей автокомпиляции
§3.6. Принципы создания автокомпилируемых моделей блоков
§3.6.2. Работа со статическими переменными блока
§3.6.2.4. Модели со структурами
Рассматривается использование структур в моделях блоков.
Структуры используются в тех случаях, когда необходимо передавать от блока к блоку несколько значений одновременно. Можно, конечно, сделать все эти значения отдельными выходами и входами блоков, но при этом пользователю придется проводить большое количество параллельных связей, которые, во-первых, загромоздят схему, и, во-вторых, повысят вероятность совершения ошибки: если пользователь забудет провести одну из этих связей, схема не будет работать, а обнаружить эту пропущенную связь в сложной схеме может оказаться не очень просто. В том случае, если все передаваемые значения имеют один и тот же тип, для этого можно использовать массив или матрицу. Например, в блоках, обрабатывающих трехмерные векторы, можно сделать входами и выходами массивы вещественных чисел. Однако, при этом разработчик модели такого блока должен все время контролировать размер массива на входе (он должен всегда содержать три элемента) и помнить, какой номер элемента какой именно координате вектора соответствует. Соответствие координат номерам элементов массива нужно помнить и пользователю, который будет работать с этими блоками: чтобы вывести на индикацию нужную ему координату, пользователь должен подключить индикатор к элементу массива с соответствующим ей номером. Если же кроме вещественных чисел между блоками должна передаваться какая-либо еще информация (например, единица измерения вектора или его текстовое название), передавать данные массивом уже не получится – все элементы массива должны иметь один и тот же тип.
Для одновременной передачи разнородных данных, каждый элемент которых имеет свое собственное имя, в RDS используются структуры. Структуры состоят из полей, которым можно дать имена, отражающие их назначение: например, структура, описывающая трехмерный вектор, может иметь поля с именами «x», «y» и «z», по названиям координатных осей. Запись «v1.x» при этом гораздо более удобна и информативна для пользователя, чем «v1[0]», которую пришлось бы использовать при передаче векторов через массив. Поля структур могут иметь любой тип, кроме массива (вместо массивов можно использовать матрицы) – ими могут быть вещественные числа, строки, матрицы и другие структуры. В программе модели и при присоединении связей имя поля структуры отделяется от имени переменной, имеющей структурный тип, точкой, как принято в языке C: обращение к полю «x» структуры, находящейся в переменной «v», записывается как «v.x». Перед первым использованием структуры в блоке ее необходимо описать в RDS при помощи пункта главного меню «» – необходимые для этого действия подробно рассмотрены в §2.14. Если в схеме уже есть хотя бы один блок, использующий данную структуру, описывать ее не нужно: описание структуры автоматически загружается вместе с любым блоком, использующим ее.
Технически работа со структурами в автокомпилируемых моделях устроена так: для любой структуры, использованной в качестве типа какой-либо переменной блока, модуль автокомпиляции создает в программе модели специальный класс доступа, содержащий внутри себя все поля этой структуры, и добавляет в класс блока rdsbcppBlockClass по одному объекту этого созданного класса для каждой переменной-структуры с таким типом. Имена этих объектов совпадают с именами переменных блока. В результате к полям структуры внутри фрагментов программ, вводимых пользователем, можно обращаться, как и было указано выше, через точку: «имя_переменной.имя_поля», где имя_переменной – это имя созданного внутри класса блока объекта для переменной, а имя_поля – имя поля специального класса доступа, созданного для поля структуры.
В качестве первого примера создадим собственную структуру для хранения трехмерного вектора и модель блока, который будет складывать два таких вектора. Начнем с описания структуры. Назовем ее «Vector3D», и создадим в ней три вещественных поля: «x», «y» и «z». Чтобы добавить в RDS новую структуру, нужно выполнить следующие шаги:
- в режиме редактирования (должна быть загружена какая-либо схема или создана новая) выбрать пункт главного меню «» – откроется окно со списком уже имеющихся в RDS структур;
- в окне структур нажать на кнопку со знаком «» в правой части окна – откроется пустое окно редактирования структуры;
- ввести в окне редактирования имя типа структуры «Vector3D» и заполнить список полей согласно рис. 375;
- закрыть окно редактирования структуры кнопкой «»;
- закрыть окно списка структур кнопкой «».
Рис. 375. Структура «Vector3D» в окне редактирования структуры
Теперь в текущей загруженной схеме есть структура с именем «Vector3D» и тремя вещественными полями. Это имя будет появляться в выпадающем списке типов при задании структуры переменных блока.
Создадим модель блока, который будет суммировать два трехмерных вектора, поступивших на входы «v1» и «v2», и выдавать результат на выход «sum». Он будет иметь следующую структуру переменных:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 1 |
| Ready | Сигнал | Выход | 0 | |
| v1 | Vector3D | Вход | ✓ | {0, 0, 0} |
| v2 | Vector3D | Вход | ✓ | {0, 0, 0} |
| sum | Vector3D | Выход | {0, 0, 0} |
Создадим новый блок с автокомпилируемой моделью, зададим для него запуск по сигналу и введем в его модель эту структуру переменных. На вкладке «» в правой части окна редактора введем следующий текст:
sum.x=v1.x+v2.x; sum.y=v1.y+v2.y; sum.z=v1.z+v2.z;
Здесь мы просто складываем одноименные поля структур в переменных v1 и v2 и присваиваем результат полю переменной sum с этим же именем. Имена полей отделяются от имен переменных точками. Как и во всех предыдущих примерах, наша модель будет запускаться автоматически при срабатывании любой связи, подключенной к входам «v1» и «v2» (в колонке «» для этих входов установлены флажки).
Рис. 376. Тестирование
модели сложения векторов
Для тестирования модели можно собрать схему, изображенную на рис. 376 (для того, чтобы к блоку было удобнее подключать связи, ему был назначен внешний вид в виде прямоугольника с текстом, после чего его размер был увеличен). Запустив расчет, можно убедиться, что векторы покомпонентно просуммированы. Может показаться, что сделав входы и выход блока структурами, мы ничего не добились: к блоку все равно подключено большое количество связей – по одной связи на каждое поле каждой структуры. Однако, это произошло только потому, что у нас нет стандартного блока, позволяющего ввести или отобразить созданную нами структуру, и мы вынуждены использовать обычные вещественные поля ввода и индикаторы, подключая их к отдельным полям структур на входах и выходе нашего блока. Если бы у нас были такие блоки (их можно, при желании, создать), к блоку подходило бы всего три связи: по одной от гипотетических блоков ввода векторов, и одна – к блоку индикации вектора.
Рис. 377. Сложение четырех векторов
Соберем еще одну схему (рис. 377), в которой будем складывать четыре вектора при помощи трех блоков с созданной нами моделью. Уже созданный блок можно размножить, копируя его в буфер обмена клавишами Ctrl + C и вставляя оттуда клавишами Ctrl + V, при этом на запрос модуля автокомпиляции о том, нужно ли создавать новую модель для копии блока, следует отвечать «». В новой схеме четыре вектора разбиты на две пары: сначала два блока слева складывают векторы внутри каждой пары, а затем точно такой же блок справа складывает получившиеся суммы между собой. Можно заметить, что от каждого из двух левых блоков-сумматоров к правому идет только одна связь: «sum → v1» и «sum → v2». Каждая из этих связей передает всю трехкомпонентную структуру «Vector3D», и это значительно удобнее, чем передавать все три ее поля отдельными связями.
Суммировать несколько векторов, строя из сумматоров каскады, как на рис. 377, не очень удобно, поэтому сделаем еще одну модель сумматора векторов, входом которого будет массив структур «Vector3D». Так мы сможем проиллюстрировать обращение к полям структур, являющихся элементами массивов. Назовем вход нашего блока «V», а выход – «sum». Если мы будем создавать этот блок в той же схеме, в которой мы работали с предыдущим, описывать структуру «Vector3D» не понадобится, поскольку она в этой схеме уже есть. Если создавать новый блок в новой схеме, нужно либо повторить в ней описание структуры, либо просто вставить в нее (например, из буфера обмена) уже созданный нами ранее блок, использующий эту структуру (структура при этом вставится в схему вместе с блоком). Потом этот блок можно стереть, описание структуры останется в схеме.
Наш новый блок будет иметь следующую структуру переменных:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 1 |
| Ready | Сигнал | Выход | 0 | |
| V | Массив Vector3D | Вход | ✓ | [ ] {0, 0, 0} |
| sum | Vector3D | Выход | {0, 0, 0} |
Создадим уже неоднократно описывавшимся способом новый блок с автокомпилируемой модельюи зададим в этой модели приведенную выше структуру переменных. На вкладке «» редактора введем следующий текст:
// Обнуление перед суммированием sum.x=sum.y=sum.z=0; // Сложение векторов в цикле for(int i=0;i<V.Size();i++) { sum.x+=V[i].x; sum.y+=V[i].y; sum.z+=V[i].z; }
Здесь мы сначала обнуляем все три поля выхода блока, а затем, в цикле по всем элементам массива, добавляем к ним одноименные поля элементов этого массива. Обращение к полю x элемента i массива V при этом выглядит обычным для языка C образом: «V[i].x».
Рис. 378. Сложение
массива векторов
Для тестирования модели соберем схему, изображенную на рис. 378. На ней девять полей ввода слева подключены к полям отдельных элементов входного массива «V». Эти поля не будут появляться в меню присоединения связи к блоку, поскольку они находятся слишком «глубоко» в иерархии переменных, поэтому при присоединении связи необходимо будет выбирать в меню пункт «» и вручную вводить имена «V[0].x», «V[0].y» и т.д. (см. §2.7.3). Это снова происходит из-за того, что у нас нет стандартного блока для ввода нашей структуры и мы вынуждены пользоваться обычными вещественными полями ввода. Если бы наш сумматор получал данные с выходов других блоков, имеющих тип «Vector3D», присоединять связи было бы проще – открывалось бы окно выбора элемента массива с заранее установленным первым свободным номером.
В обоих примерах полями структуры были вещественные числа. Если бы полями были другие структуры или матрицы, индексы элементов матриц или поля этих вложенных структур записывались бы, как обычно, после имени поля-структуры или поля-матрицы. Представим себе, например, некоторую структуру с именем «SomeStruct», имеющую поля «vector» типа «Vector3D» и «vmatr» типа «матрица Vector3D» (рис. 379).
Рис. 379. Гипотетическая структура «SomeStruct»
Пусть в каком-либо блоке с автокомпилируемой моделью есть переменная «var» типа «SomeStruct». В программе модели этого блока к отдельным полям и элементам этой сложной переменной можно обращаться следующим образом:
- «var.vector» – поле vector переменной var (это поле будет структурой типа «Vector3D»);
- «var.vector.y» – вещественное поле y структуры, находящейся в поле vector переменной var;
- «var.vmatr[1][2]» – структура типа «Vector3D», находящаяся в строке 1 и столбце 2 матрицы из поля vmatr переменной var;
- «var.vmatr.Rows()» – число строк матрицы, находящейся в поле vmatr переменной var;
- «var.vmatr[1][2].x» – вещественное поле x структуры типа «Vector3D», находящейся в строке 1 и столбце 2 матрицы из поля vmatr переменной var;
- и т.д.
Таким образом, способ обращения к полям структур во всех этих случаях ничем не отличается от стандартного синтаксиса языка C/C++.