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

Описания RDS

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

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

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

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

§3.6.2.7. Использование входов со связанными сигналами

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

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

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

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

##### §3.6.2.7. Использование входов со связанными сигналами

Описывается работа с входами блока, для которых заданы связанные сигналы – по этим сигналам можно понять, какие из входов блока сработали в данном такте расчета.

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

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

Для решения этой задачи в RDS встроен механизм, позволяющий модели блока узнать, какие именно входы получили по связям новые значения в конце предыдущего такта расчета. Для этого в редакторе переменных необходимо связать вход блока с переменной сигнального типа (она может быть входом, выходом или внутренней), задав для этого входа вместо роли «вход» роль «вход/сигнал» и указав имя связанной сигнальной переменной. При срабатывании связи, подключенной к этому входу, в связанную с ним сигнальную переменную автоматически запишется единица. Разумеется, если необходимо запустить модель при срабатывании связи, флажок «пуск» у этого входа тоже должен быть установлен. Таким образом, модель блока может проверить значения связанных с входами сигналов и, обнаружив в некоторых из них единицы, понять, какие именно входы получили значения по связям в прошлом такте расчета. Выполнив действия, связанные с изменившимися входами, модель должна обнулить все связанные сигналы, подготовив их к следующему срабатыванию связей. Как и любые сигнальные переменные, связанные сигналы не могут получить нулевое значение при срабатывании связей – получив значение 1, сигнал сохраняет его до тех пор, пока модель блока самостоятельно не обнулит переменную (см. §3.6.2.6). На самом деле, единица в связанной сигнальной переменной указывает не на изменение значения входа, а на то, что сработала связь, подключенная к этому входу: если по этой связи придет то же самое значение, связанный сигнал все равно взведется. Однако, поскольку модели блоков чаще всего пишут так, чтобы они срабатывали и активировали свои выходные связи только при необходимости передать новые значения, срабатывание присоединенной ко входу связи с большой вероятностью говорит о том, что что-то изменилось. В любом случае, обработка значения входа при срабатывании именно присоединенной к нему связи меньше нагружает систему, чем та же самая обработка при срабатывании любой связи, присоединенной к блоку. Несколько входов можно, при желании, связать с одним сигналом, если модель блока должна выполнять одинаковые действия при изменении любого из этих входов.

В качестве примера использования связанных сигналов создадим модель блока, входами которого будут вещественные матрицы «M1» и «M2», а выходом «y» – произведение минимального элемента матрицы «M1» и максимального элемента матрицы «M2» (именно эта модель приводится в примере в §2.5.7 руководства программиста). Определение максимального и минимального элемента матриц требует перебора всех их значений, поэтому мы будем запоминать эти значения во вспомогательных переменных «M1min» и «M2max», вычисляя их только при срабатывании связей, соединенных с входами «M1» и «M2» соответственно. Вход «M1» мы свяжем с сигналом «s1», вход «M2» – с сигналом «s2», по появлению единиц в этих сигналах мы будем узнавать о срабатывании соответствующих входных связей.

Таким образом, наш блок будет иметь следующую структуру переменных:

Имя | Тип | Вход/выход | Пуск | Начальное значение
--- | --- | --- | --- | ---
Start | Сигнал | Вход | ✓ | 1
Ready | Сигнал | Выход |  | 0
s1 | Сигнал | Внутренняя |  | 1
s2 | Сигнал | Внутренняя |  | 1
M1 | Матрица double | Вход/сигнал «s1» | ✓ | [ ] 0
M2 | Матрица double | Вход/сигнал «s2» | ✓ | [ ] 0
M1min | double | Внутренняя |  | ?
M2max | double | Внутренняя |  | ?
y | double | Выход |  | ?

Следует обратить внимание на то, что в качестве начальных значений переменных «M1min», «M2max» и «y» указан вопросительный знак, то есть специальное значение, используемое в качестве индикатора ошибки вычисления (в модели его можно получить из глобальной переменной rdsbcppHugeDouble). Действительно, пока максимальный элемент «M2» и минимальный элемент «M1» не вычислены, мы ничего не можем присвоить ни внутренним переменным «M1min» и «M2max», ни их произведению «y».

В этой структуре переменных сигналы «s1» и «s2» расположены до входов «M1» и «M2», с которыми они связаны. Это не обязательно, но так удобнее редактировать переменные: при указании в редакторе для входа роли «вход/сигнал», имя связанного сигнала выбирается из выпадающего списка, в который включаются уже введенные на данный момент переменные сигнального типа, поэтому лучше, чтобы на момент ввода входа сигнал, который с ним будет связан, уже был введен в редактор переменных. Если, по каким-либо причинам, необходимо поместить связанные сигналы после входов, с которыми они связываются (например, если эти связанные сигналы – тоже входы блока, и разработчик хочет, чтобы в меню присоединения связей они были в конце), можно сначала ввести в редакторе переменных сигналы, а потом вставить входы в нужное место списка (см. §2.9.2).

Сигналам «s1» и «s2» даны единичные начальные значения, чтобы при самом первом запуске расчета модель блока обработала исходные значения матриц-входов «M1» и «M2», как будто связи, подключенные к ним, сработали перед запуском.

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

      if(s1) // Сработала связь к M1
        { s1=0; // Обнуление сигнала
          // Ищем минимальный элемент в M1 и записываем в M1min
          M1min=rdsbcppHugeDouble;
          for(int r=0;r<M1.Rows();r++)
            for(int c=0;c<M1.Cols();c++)
              if(M1min==rdsbcppHugeDouble || M1min>M1[r][c])
                M1min=M1[r][c];
        }
      if(s2) // Сработала связь к M2
        { s2=0; // Обнуление сигнала
          // Ищем максимальный элемент в M2 и записываем в M2max
          M2max=rdsbcppHugeDouble;
          for(int r=0;r<M2.Rows();r++)
            for(int c=0;c<M2.Cols();c++)
              if(M2max==rdsbcppHugeDouble || M2max<M2[r][c])
                M2max=M2[r][c];
        }
      // Перемножаем максимальный и минимальный элементы матриц
      if(M1min!=rdsbcppHugeDouble && M2max!=rdsbcppHugeDouble)
        y=M1min*M2max;
      else
        y=rdsbcppHugeDouble;

Эта программа состоит из трех частей. Сначала мы проверяем переменную s1 – если ее значение не нулевое, значит, сработала связь, подключенная ко входу M1, и мы перебираем все элементы матрицы M1 (работа с матрицами описана в §3.6.2.2), ищем среди них минимальный и записываем во внутреннюю переменную M1min. Значение s1 мы при этом обнуляем – кроме модели блока, это сделать некому. Затем, точно так же, при ненулевом значении s2 мы находим максимальное значение M2 и записываем его в M2max. Если какая-либо из входных матриц останется пустой, в соответствующей ей внутренней переменной останется значение rdsbcppHugeDouble, присвоенное ей перед циклом: для пустой матрицы значение максимального или минимального элемента не может быть определено, поэтому мы пишем в переменную значение-индикатор ошибки. В конце программы мы перемножаем M1min и M2max, если обе они не равны rdsbcppHugeDouble, то есть если определен и минимальный элемент первой матрицы, и максимальный элемент второй. В противном случае мы выдаем на выход значение-индикатор ошибки rdsbcppHugeDouble.

Таким образом, единственная операция, всегда выполняющаяся в такте расчета – это перемножение M1min и M2max. Поиск минимального элемента в M1 выполняется только при срабатывании подключенной к ней связи, так же как и поиск максимального элемента M2. Если значение на одном из входов меняется редко, перебор значений этой матрицы тоже будет выполняться редко, что увеличит производительность системы. В данном случае выигрыш будет незначительным, однако, если бы вычисления с матрицей были сложнее (например, необходимо было бы вычислять ее определитель или обратную ей матрицу), прирост быстродействия был бы ощутимым.

Для тестирования созданной модели можно собрать схему, изображенную на рис. 385. В ней к входам нашего блока присоединены стандартные блоки ввода матриц, а к выходу – числовой индикатор. Запустив расчет и изменяя значения в матрицах, можно наблюдать изменения на индикаторе.

Рис. 385. Тестирование модели, вычисляющей произведение минимального
элемента одной матрицы и максимального элемента другой