Текущий раздел:

Описания RDS

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

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

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

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

§3.6.2.5. Модели со строками

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

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

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

#### §3.6.2. Работа со статическими переменными блока

##### §3.6.2.5. Модели со строками

Рассматривается использование текстовых строк в переменных автокомпилируемых блоков. Описываются операторы и функции-члены класса для работы со строками.

Строки используются в качестве переменных блоков достаточно редко. Как правило, схемы, собираемые в RDS, предназначены для моделирования каких-либо технических процессов, в которых главную роль играют операции с числами, массивами и матрицами, которые могут сами быть входами и выходами блоков или содержаться внутри каких-либо структур. Тем не менее, RDS позволяет передавать строки по связям и работать с ними в моделях блоков. Чаще всего переменные-строки используются в блоках, отображающих какие-либо данные при помощи своих векторных картинок: блоки текста, являющиеся графическими элементами таких векторных картинок, могут быть связаны с переменными блока и автоматически отображать их значения. Если связать такой текстовый блок с переменной-строкой, на нем можно будет выводить любой текст по желанию разработчика модели, для этого достаточно присвоить нужный текст этой переменной.

Для работы со строками в автокомпилируемых моделях используется специальный класс с именем rdsbcppString. Для каждой переменной-строки в класс блока rdsbcppBlockClass добавляется по одному объекту класса rdsbcppString, через который и осуществляется работа с этой переменной. Имена этих объектов совпадают с именами переменных блока, поэтому внутри фрагментов программ, вводимых пользователем, к строкам, являющимся статическими переменными блока, можно обращаться просто по именам. Класс rdsbcppString имеет несколько переопределенных операторов и функций-членов, облегчающих работу со строками (во всех примерах далее будем считать, что str, str1 и str2 – это переменные-строки какого-либо блока):

char *c_str(void) char *c_strA(void)
  Возвращает указатель на стандартную строку «char*» в кодировке UTF8, хранящуюся внутри переменной блока (тип «char*» используется в большинстве стандартных функция языка C, работающих со строками). Если строка пуста, возвращает указатель на статическую пустую строку, то есть эта функция никогда не возвращает значение NULL, даже если строка в переменной блока отсутствует. Имена c_str и c_strA – синонимы. Пример использования функции:

        // Преобразование строки str в целое число n
        int n=atoi(str.c_strA());

char *c_strW( void)
  Возвращает указатель на вспомогательную строку «wchar_t*» в кодировке UTF16, хранящуюся внутри объекта класса rdsbcppString (эта кодировка используется в функциях Windows API). Если строка пуста, возвращает указатель на статическую пустую строку, то есть эта функция никогда не возвращает значение NULL, даже если строка в переменной блока отсутствует. Объект типа rdsbcppString формирует эту внутреннюю строку в кодировке UTF16 при первом обращении к ней и далее обеспечивает синхронизацию ее содержимого с «основной» строкой в переменной блока, имеющей кодировку UTF8.
BOOL IsEmpty(void)
  Возвращает TRUE, если строка пуста (не имеет символов) и FALSE в противном случае. Пример использования функции:

        // Преобразование строки str в целое число n
        if(str.IsEmpty())
          { // Строка str пуста
            …
          }

int Length(void)
  Возвращает число символов в строке. Для пустой строки возвращается ноль. Пример использования функции:

        int len=str.Length();

char & operator[](int n)
  Байт строки номер n (нумерация начинается с нуля). Байт в строке в кодировке UTF8 может быть кодом символа, если это символ ASCII, или частью кода символа, если это символ национального алфавита. Проверка допустимости индекса n не производится, попытка обратиться к байту за пределами строки (а также к любому байту в пустой строке) вызовет критическую ошибку. Следует учитывать, что строки, как и положено в языке C, завершаются нулевым байтом. Выражение с этим оператором может находиться как в левой, так и в правой части оператора присваивания, то есть можно не только получать значения отдельных байтов в строке, но и присваивать их. Примеры использования оператора:

        // Запись первого байта строки str в переменную c
        char c;
        if(str.IsEmpty()) c=0;
        else c=str[0];
        // Замена всех байтов в строке str на пробелы
        for(int i=0;i<str.Length();i++)
          str[i]=' ';

rdsbcppString operator+(const rdsbcppString &val)
  Переопределенный оператор «+», позволяющий складывать строки-переменные блоков. Пример использования оператора:

        str=str1+str2;

rdsbcppString operator+(char *val)
  Переопределенный оператор «+», позволяющий складывать строку-переменную блока и обычную строку «char*» в кодировке UTF8. Пример использования оператора:

        str=str1+"текст";

rdsbcppString & operator+=(const rdsbcppString &val)
  Оператор «+=», позволяющий добавить к концу строки в переменной блока строку из другой переменной. Пример использования оператора:

        str+=str1;

rdsbcppString & operator+=(char *val)
  Оператор «+=», позволяющий добавить к концу строки-переменной блока обычную строку «char*» в кодировке UTF8. Пример использования оператора:

        str+="текст";

rdsbcppString & operator=(const rdsbcppString &val)
  Оператор присваивания, копирующий в данную переменную блока строку из переменной val. Пример использования оператора:

        str=str1;

rdsbcppString & operator=(char *val)
  Оператор присваивания, копирующий в данную переменную блока текст val в кодировке UTF8. Пример использования оператора:

        str="текст";

Практически все интерфейсные функции RDS поддерживают работу как со строками в кодировке UTF8, которая совместима со стандартными функциями обработки строк язвка C (strcat, strlen и т.п.) и используется в переменных блоков, так и в кодировке UTF16, которая используется в Windows API. Однако, класс rdsbcppString поддерживает только UTF8, поскольку основное его предназначение – работа со строковыми переменными блоков RDS, в которых UTF16 не используется. Тем не менее, функция класса c_strW() позволяет перевести строку в UTF16 для дальнейшего использования в функциях Windows, например, при открытии файла с именем, записанным в строке.

В большинстве случаев работа со строками в модели блока производится следующим образом: при помощи оператора c_str() считываются строки из входных переменных, затем при помощи стандартных функций библиотек C с этими строками выполняются какие-либо действия, результаты которых присваиваются выходным переменным блока. Если необходимо просто собирать длинный текст из нескольких строк, вместо библиотечных функций C можно использовать оператор сложения, переопределенный в классе строки rdsbcppString.

В качестве примера создадим блок, который будет отображать строку, представляющую собой разделенный запятыми список всех элементов его входного вещественного массива «X», число знаков после десятичной точки в которых будет определяться целым входом «Dec». Переменную блока, в которой будет записана сформированная строка, назовем «str», ее можно сделать внутренней переменной или выходом блока. Таким образом, структура переменных у нашего блока будет такая:

Имя | Тип | Вход/выход | Пуск | Начальное значение
--- | --- | --- | --- | ---
Start | Сигнал | Вход | ✓ | 1
Ready | Сигнал | Выход |  | 0
X | Массив double | Вход | ✓ | [ ] 0
Dec | int | Вход | ✓ | 0
str | Строка | Выход |  |

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

      char buf[100]; // Вспомогательный массив

      if(X.IsEmpty()) // Массив пуст
        { str=""; // Отображаем пустую строку
          return;
        }
      // В массиве есть элементы

      if(Dec>10) // Выводим не более 10 символов после точки
        Dec=10;

      str="("; // Список начнется со скобки
      for(int i=0;i<X.Size();i++) // Цикл по всем элементам
        { if(i) // Если не первый элемент, добавляем запятую
            str+=", ";
          if(X[i]==rdsbcppHugeDouble) // Признак ошибки
            str+="?";
          else
            { // Формируем текст в массиве buf
              sprintf(buf,"%.*lf",(int)Dec,(double)X[i]);
              str+=buf; // Дописываем buf к str
            }
        }
      str+=")"; // Закрывающая скобка списка

В этом фрагменте программы для преобразования вещественного числа в текст с заданной точностью используется стандартная библиотечная функция sprintf, описанная в файле «stdio.h». Этот файл не включается в формируемый модулем автокомпиляции текст программы по умолчанию, поэтому директиву его включения необходимо записать вручную в раздел глобальных описаний. Для этого в редакторе модели необходимо выполнить следующие действия:

- на дополнительной левой панели редактора выбрать вкладку «события»;
- на этой вкладке раскрыть раздел «описания» (это самый первый раздел в списке), щелкнув левой кнопкой мыши на значке «+» слева от него;
- дважды щелкнуть на открывшемся подразделе «глобальные описания», после чего в правой части окна редактора рядом с вкладкой «модель» появится новая вкладка «описания».

На открывшейся вкладке «описания» следует ввести следующий текст из одной строки:

      #include <stdio.h>

Теперь, когда директива включения недостающего файла заголовков добавлена, рассмотрим приведенную выше программу, введенную на вкладке «модель». Эта программа будет выполняться при каждом срабатывании связи, подключенной к любому из входов блока, то есть при изменении содержимого входного массива X или точности отображения его элементов Dec.

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

Далее, если во входном массиве X нет ни одного элемента (X.IsEmpty() вернет TRUE), переменной str присваивается пустая строка и выполнение реакции завершается оператором return – массив пуст, и отображать нечего. В противном случае значение числа символов после десятичной точки Dec ограничивается значением 10: если значение переменной Dec больше 10, ей принудительно присваивается 10. Это ограничение сделано для того, чтобы строки, в которые преобразуются значения каждого элемента массива X, гарантированно не превысили размеров массива buf. Можно было бы написать программу так, чтобы такое ограничение не потребовалось (например, с использованием сервисной функции RDS rdsDtoA), но мы не будем усложнять этот пример. Можно заметить, что мы присваиваем значение переменной, являющейся входом блока – это вполне допустимо, присвоенное значение останется в переменной Dec до следующего срабатывания подключенной к ней связи.

Далее мы присваиваем переменной str открывающую круглую скобку, а затем, в цикле по всем элементам массива X (см. §3.6.2.3), добавляем в конец этой строки при помощи оператора «+=» текст для каждого элемента. Внутри этого цикла мы для каждого элемента, кроме первого (то есть при значении счетчика цикла i, не равном нулю), добавляем к строке запятую и пробел, отделяющий элементы друг от друга, и, если X[i] не равно значению ошибки rdsbcppHugeDouble, формируем в массиве buf текстовое представление вещественного числа с заданной точностью и добавляем этот текст к str (для значения ошибки вместо значения элемента добавляется вопросительный знак). После цикла к str добавляется закрывающая круглая скобка.

Вещественное число в цикле преобразуется в строку в массиве buf при помощи функции sprintf:

      sprintf(buf,"%.*lf",(int)Dec,(double)X[i]);

Строка формата «%.*lf» указывает на то, что выполняется преобразование вещественного числа двойной точности («lf») с числом знаков после десятичной точки, указанным в параметре функции перед самим числом («.*»). В третьем и четвертом параметрах sprintf передаются точность представления числа Dec и само число X[i]. Поскольку sprintf – функция с переменным числом параметров, тип которых явно не указан в ее описании, компилятор не сможет самостоятельно преобразовать типы объектов, созданных модулем автокомпиляции для переменных блока (напомним, что, например, Dec – это именно объект некоторого специально сформированного класса, а не переменная типа int). Чтобы избежать разночтений, для Dec и X[i] явно вызываются операторы преобразования к типам int и double соответственно (см. также §3.6.1).

Мы создали модель блока, преобразующую массив вещественных чисел в строку со значениями всех его элементов, но сам блок пока ничего отображать не может – он выглядит как пустой белый квадрат. Чтобы строка появлялась на изображении блока, необходимо задать для него векторную картинку. Создадим картинку, состоящую из прямоугольника и размещенного внутри него блока текста, связанного с переменной блока str (рис. 380). Картинку можно ввести из редактора модели через команду установки параметров блоков или в окне параметров созданного нами блока. В последнем случае необходимо выполнить следующие шаги:

Рис. 380. Внешний вид блока
в редакторе картинки

- в режиме редактирования нажать на изображении блока правую кнопку мыши и выбрать в контекстном меню пункт «параметры» – откроется окно параметров блока;
- на вкладке окна «внешний вид» установить флажок «определяется картинкой» и нажать кнопку «изменить» на панели «картинка» в левой нижней части вкладки – откроется редактор картинки;
- в редакторе картинки создать прямоугольник и блок текста, расположить блок текста поверх и внутри прямоугольника, как на рис. 380;
- дважды щелкнуть левой кнопкой мыши на блоке текста, выбрать в открывшемся окне его параметров вкладку «связи» и выбрать в выпадающем списке на этой вкладке переменную «str» (если это имя в выпадающем списке будет отсутствовать, а это возможно, если мы еще не компилировали модель, имя можно ввести вручную);
- закрыть окно параметров текста кнопкой «OK»;
- закрыть окно редактора (как обычно в Windows, кнопкой с крестиком в правой верхней части окна);
- закрыть окно параметров блока кнопкой «OK».

Теперь наш блок в режимах моделирования и расчета будет отображать значение переменной «str». Для тестирования созданной нами модели можно собрать схему, изображенную на рис. 381. В ней к входному массиву блока «X» подключен стандартный блок ввода матриц (в RDS матрицы можно соединять с массивами), а к целому входу «Dec» – обычное поле ввода. Задав на входе «X» какую-нибудь матрицу-строку и запустив расчет, на изображении блока можно будет увидеть текст из строки «str», то есть элементы этой матрицы, перечисленные через запятую. Если размеров блока недостаточно для отображения строки, она будет обрезана по границам блока текста, заданного в редакторе картинки – в этом случае можно снова открыть редактор картинки блока и изменить размеры ее элементов.

Рис. 381. Тестирование модели преобразования массива в строку

Следует иметь в виду, что строка в RDS – это всегда последовательность символов в кодировке UTF8, завершающаяся нулевым байтом.