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

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

Глава 2. Создание моделей блоков

§2.5. Статические переменные блоков

§2.5.5. Работа со структурами

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

Достаточно часто необходимо передавать от блока к блоку несколько значений одновременно. Например, если блоки моделируют поведение каких-либо объектов в трехмерном пространстве, им нужно передавать друг другу координаты этих объектов, то есть шесть вещественных чисел (три числа описывают положение центра объекта в пространстве, и еще три – углы его поворота). Делать каждую их шести координат отдельным входом или выходом не очень удобно: для передачи информации об объекте между двумя блоками их придется соединить шестью связями – это загромождает схему, особенно если таких блоков много, и нагружает пользователя лишней работой. Можно записывать координаты в массивы: массив вещественных чисел из шести элементов может хранить все шесть координат объекта и передаваться между блоками как единое целое (пользователю потребуется провести только одну связь). Однако, при этом программист должен постоянно следить за размером переданного массива и помнить соответствие между координатами объекта и номерами элементов массива. Помнить это соответствие придется и пользователю: если он, например, захочет вывести координату «X» на числовой индикатор, он должен знать номер элемента массива, в котором эта координата находится. А если кроме шести вещественных координат необходимо передавать еще какую-либо информацию об объекте (например, строку с его названием), использование массивов становится невозможным: все элементы массива должны иметь один и тот же тип.

Решение этой проблемы давно известно во всех языках программирования высокого уровня: для хранения и передачи набора разнородных данных обычно используются структуры (в некоторых языках их называют «записями»), которые состоят из полей разного типа, каждое из которых имеет свое собственное имя. Имена полей существенно облегчают работу и пользователю, и программисту: запись «Coords.X» гораздо понятнее записи «Coords[0]». RDS позволяет создавать в блоках переменные структурного типа, в качестве полей они могут содержать как простые переменные, так и массивы и другие структуры. По связям структура всегда передается как единое целое, этим структуры отличаются от шин: каналы передачи данных в шинах тоже имеют свои имена, как и поля структур, но работают полностью независимо, в структуре же нельзя передать по связи только одно поле.

Для структуры в дереве переменных хранится указатель на область памяти, в которой последовательно размещаются ее поля (рис. 24).

Размещение в памяти данных структуры

Рис. 24. Размещение в памяти данных структуры

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

Для примера введем в RDS структуру TestComplex, описывающую комплексное число и состоящую из двух полей Re и Im типа double:

Смещение Имя Тип Размер По умолчанию
0 Re double 8 0
8 Im double 8 0
Окно списка структур схемы

Рис. 25. Окно списка структур схемы

Для редактирования и создания новых структур служит пункт меню «система | структуры» (рис. 25), он открывает окно со списком структур, содержащим имена всех созданных в схеме структур и число блоков схемы, использующих каждую структуру в данный момент (при попытке изменить структуру, которая в данный момент используется, RDS выведет предупреждение, так как добавление, удаление и перестановка полей в структуре может привести к неработоспособности блоков, модели которых ссылаются на ее поля по фиксированным смещениям).

В окне списка структур можно добавить новую структуру (кнопка «+») и изменить уже существующую (кнопка «{…}»). Нам нужно создать новую структуру – после нажатия кнопки «+» откроется окно редактора структур (рис. 26), похожее на окно редактора переменных блока. Фактически, эти два окна отличаются только тем, что в редакторе структур нельзя сделать поле структуры входом или выходом (входом или выходом блока может быть только вся структура целиком), задать запуск модели блока, связанные логические и сигнальные переменные, а также отображение имени в точке присоединения связи (см. §1.5) – все эти параметры структура вместе со всеми своими полями получит, когда она будет использована в каком-либо блоке в качестве входа или выхода. В окне редактора структур нужно задать имя нашей структуры («TestComplex») ввести два вещественных поля «Re» и «Im», и запомнить структуру нажатием кнопки «OK». Теперь структура «TestComplex» зарегистрирована в схеме и может быть указана в качестве типа любой статической переменной любого блока.

Окно редактирования полей структуры

Рис. 26. Окно редактирования полей структуры

Создадим модель блока, суммирующего значения комплексных (типа «TestComplex») входов x1 и x2 и выдающего сумму на выход y. Блок будет иметь следующую структуру переменных:

Смещение Имя Тип Размер Вход/выход Пуск Начальное значение
0 Start Сигнал 1 Вход 1
1 Ready Сигнал 1 Выход 0
2 x1 Структура TestComplex 8 Вход {0,0}
10 x2 Структура TestComplex 8 Вход {0,0}
18 y Структура TestComplex 8 Выход {0,0}

Функция модели блока будет довольно простой:

  extern "C" __declspec(dllexport)
    int  TestStructSum(int CallMode,
                               BlockData,
                               ExtParam)
  {
  // 
  #define pStart ((char *)(BlockData->VarTreeData))
  #define Start (*((char *)(pStart)))
  #define Ready (*((char *)(pStart+RDS_VSZ_S)))
  #define x1 (*((void **)(pStart+2*RDS_VSZ_S)))
  #define x2 (*((void **)(pStart+2*RDS_VSZ_S+RDS_VSZ_STRUCT)))
  #define y (*((void **)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_STRUCT)))
  // Макроопределения для полей структуры TestComplex
  #define Re(base) (*((double *)(base)))
  #define Im(base) (*((double *)(((char*)(base))+RDS_VSZ_D)))

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

        // Выполнение такта моделирования
        case :
          Re(y)=Re(x1)+Re(x2);
          Im(y)=Im(x1)+Im(x2);
          break;
      }
    return ;
  // Отмена макроопределений
  #undef Im
  #undef Re
  #undef y
  #undef x2
  #undef x1
  #undef Ready
  #undef Start
  #undef pStart
  }
  //=========================================

Макроопределения для структур x1, x2 и y представляют собой указатели универсального типа (void*) на начало области данных каждой структуры. В отличие от определений для переменных блока, которые жестко привязаны к началу дерева переменных BlockData->VarTreeData, определения для полей структуры Re и Im – это макросы с параметром, через который передается указатель на данные конкретной структуры. Например, для доступа к полю Re структуры x1 необходимо написать Re(x1) (рис. 27).

Обращение к полям структуры из модели блока

Рис. 27. Обращение к полям структуры из модели блока

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

Строка типа, передаваемая функции модели при вызове с параметром RDS_BFM_VARCHECK, состоит из двух букв «S» для обязательных сигналов Start и Ready, и трех фрагментов «{DD}», описывающих тип «TestComplex» для переменных x1, x2 и y. При проверке переменных название типа структуры не имеет значения. Важно только то, что структура состоит из двух полей double (два символа «D»). Если, например, ввести в RDS структуру «TestStruct», состоящую из двух полей Field1 и Field2 типа double, и заменить в описываемом блоке тип «TestComplex» на «TestStruct», в модель будет передаваться точно такая же строка типа. Обе структуры будут описываться строкой «{DD}», и модель не увидит между ними разницы. Тем не менее, это не приведет к возникновению ошибок, поскольку структуры с одинаковыми типами полей размещаются в памяти одинаково. Макрос Re(x1) может с одинаковым успехом обращаться к полю Re, если переменная x1 имеет тип «TestComplex», и к полю Field1, если x1 имеет тип «TestStruct», поскольку эти поля имеют одинаковые смещение и размер. Эта модель, предназначенная для суммирования комплексных чисел, то есть одноименных полей структуры «TestComplex», сможет суммировать и одноименные поля структуры «TestStruct» (целесообразность такого суммирования остается на совести пользователя, заменившего одну структуру на другую).

При вызове модели с параметром RDS_BFM_MODEL в поля Re и Im выхода y записывается сумма одноименных полей входов x1 и x2. Макросы для полей структур, как и макросы статических переменных, могут находиться и в левой, и в правой части выражения. Чтобы присвоить «y.Re» сумму «x1.Re» и «x2.Re», нужно записать

  Re(y)=Re(x1)+Re(x2)

Точно так же записывается суммирование полей Im. Для упрощения примера мы здесь не проверяем допустимость сложения полей структуры. Одно из них может оказаться равным специальному значению, сигнализирующему об ошибке вычисления, возникшей в предыдущем блоке (оно возвращается функцией rdsGetHugeDouble) – в этом случае выполнять сложение вещественных чисел нельзя, вместо этого соответствующему полу выхода y следует присвоить это же значение-индикатор ошибки.

Схема для проверки работы со структурами

Рис. 28. Схема для проверки
работы со структурами

Поскольку структуру «TestComplex» мы только что создали для этого примера, единственная модель, которая с ней работает – это написанная нами TestStructSum. Хотя у нас нет других блоков и моделей, поддерживающих нашу новую структуру, проверить работу нового блока мы все равно можем: RDS позволяет подключать связи не только ко всей структуре как к единой переменной, но и к отдельным ее полям. Можно собрать схему, изображенную на рис. 28: к полям Re и Im входов блока x1 и x2 подключены поля ввода, а к полям выхода y – индикаторы. При запущенном расчете на индикаторе, соединенном с «y.Re» должна отображаться сумма «x1.Re» и «x2.Re», а на индикаторе, соединенном с «y.Im» – сумма «x1.Im» и «x2.Im».

Изображенная на рис. 28 схема не показывает преимущества структур перед отдельными переменными – как было указано выше, у нас пока есть только один блок, работающий со структурой «TestComplex», поэтому все связи в схеме присоединяются к отдельным полям структур. Однако, выход этого блока мы уже можем соединить с входом другого такого же (например, чтобы добавить к вычисленной сумме третье комплексное число), и для этого потребуется всего одна связь. Мы также можем ввести выход блока в какую-либо подсистему через один внешний вход типа «TestComplex», подключить его к одному каналу шины этого же типа и т.п. – нам теперь не нужно создавать пару связей для каждой передачи комплексного числа.


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