SoftCraft
разноликое программирование

Top.Mail.Ru

Искусство программирования ... RS-триггера?!

В. C. Любченко

Можно скачать текст статьи в формате html (zip архив около 14 кб)

Часть 2. Программирование

Часть 1. Электронная схема и формальные модели
Часть 3. Тест на синхронную работу

"Что касается изучения физического мира, то математика не предлагает ничего, кроме теорий или моделей. Всякий раз, когда накопленный нами опыт или специальный эксперимент показывает, что новая теория дает более точное описание реальности, чем старая, старую теорию вполне допустимо заменить новой." (стр. 115)
Клайн М. Математика. Утрата неопределенности.

Формальные модели триггера мы создали (см. [1]). Теперь черед их программной реализации. Но, правда, лишь тех моделей, которые этому "поддаются" легко в рамках существующих возможностей. Например, мы не будет рассматривать реализацию сетей Петри, ограничившись в их случае лишь формальной моделью. Сегодня программисты редко создают модели программ, предпочитая сразу писать тексты. Но это не значит, что модели нет. Строго программа - это модель, но только в определенной форме представления. Так, если та же блок-схема - графическая форма модели, то программа - ее текстовая форма, выраженная на определенном языке программирования. Дальнейшая наша цель заключается на основе программной реализации и результатов тестирования полученных программ выбрать из нескольких формальных моделей те модели, которые удобно, а, главное, адекватно описывают параллельные системы. Оценку той или иной модели будем делать, анализируя варианты реализаций разработанных ранее в [1] моделей RS-триггера.

1. Модель блок-схем

Блок-схемы (БС), несмотря на снисходительное к ним отношение, остаются основной программной моделью, т.к. почти все управляющие конструкции языков программирования имеют свой графический аналог в БС. Все основные универсальные языки программирования, и известные и, пожалуй, вновь создаваемые, пока следуют так называемой процедурной модели программ, в рамках которой графической форма представления алгоритмов имеет вид блок-схем.

1.1. Flora/C+

Система программирования Flora/C+ и одноименная технология проектирования программ являются разработкой компании Compass Plus (http://www.compassplus.ru). Если довериться описанию, то это "объектно-ориентированная, мультизадачная, переносимая, распределенная, открытая, визуальная среда разработки и исполнения приложений. Она предназначена для создания систем мониторинга и управления технологическими процессами, многоуровневых распределенных АСУТП и АСУП, on-line обработки транзакций, систем типа "клиент-сервер" и т.д." (ее детальное описание представлено http://www.compassplus.ru/flora/docs/index.htm).

В основе системы программирования Флора лежит процедурный язык близкий к языку С++. Флора обладает качествами, которые позволяют быстро "набросать" работоспособное приложение. Ее среда примечательна еще и тем, что в ней можно легко реализовать как последовательную, так и параллельную модель триггера. Таким образом, Флора очень удобна для реализации и демонстрации идей, представленных моделями триггера на базе БС.

1.1.1. Последовательная модель RS-триггера

Создадим сначала последовательную модель триггера в форме БС( см. рис.3 в [1]). Для этого создадим объект типа task (см. объект RS_LWS в проекте RS_LWS.ots), код которого представлен в листинге 1. Кроме этого в проект необходимо включить объекты, соответствующие обычным переменным, на которые будут ссылаться переменные объекта task и объекты окна диалога управления программой. В нашем случае это объекты типа Int с именами r, s, q, nq. Они будут отражать состояние входов/выходов триггера, а диалог организуют с помощью объектов типа FigButton (кнопка), FigEditLine (строчный редактор), FigCheck (кнопка-флаг), которые также будут иметь связи с этими объектами-переменными.

Собственно программирование в данном случае заключается в создании текста программы для объекта task, а все остальное: создание объектов-переменных, установка их свойств и ссылок на переменные, создание окна диалога, его элементов и их настройка выполняется с помощью визуальных средств среды Flora/C+.

После настройки элементов проекта нажимаем Ctrl+F9 и анализируем результат, управляя приложением из окна диалога. Проще - некуда. Возможно, было бы проще при более развитых визуальных средствах проектирования, но, как говорится, дело наживное. Пока же и так достаточно просто (особенно если проект несложный). Разработчики Флоры добились, что при наличии определенных навыков, создать приложение, состоящее даже из множества параллельно функционирующих частей можно просто и быстро. Но так ли просто добиться и его правильной работы - мы далее еще рассмотрим.

1.1.2 Параллельная модель RS-триггера

Последовательную модель триггера мы создали. Построим теперь его параллельную модель. Это даже проще. Для этого в новом проекте (см. проект V3\RS.ots) заменим ранее созданный объект task, реализующий триггер, двумя параллельными, объектами, каждый из которых реализует модель элемента И-НЕ. Это будут те же процессы типа task, например, с именами taskR, taskS. Коды у них одинаковы, как одинаковы элементы триггера (см. листинг 2).

Отличие проекта RS.ots от проекта RS_LWS.ots состоит в использовании другой модели триггера. Видно, что объектов стало больше, но их код стал несравненно проще (см. листинги 1 и 2). Именно в этом заключается вся "прелесть" параллельного программирования: объектов - больше, а код - проще. И чем сложнее проект, тем значительнее выигрыш. Но чтобы это утверждение стало истиной, необходимо соблюдение важного условия - корректной реализации параллелизма. Только в этом случае последовательная модель и параллельная модель будут работать одинаково, выдавая один и тот же результат при одном и том же наборе входных данных.

1.1.3. Тестирование

Тестирование демонстрирует на первый взгляд одинаковую работу созданных моделей. Подавая на их входы различные комбинации входных сигналов через объекты диалога типа FigCheck, устанавливая переменные r и s, мы получаем для обеих моделей один и тоже результат. При этом значение выходов отображают объекты, составленные переменным q, nq.

Но триггер система динамическая, а потому требует анализа своего поведения в динамике. Кроме того, для него важна в этом случае не просто статическая комбинация на его входах, а временное соотношение между установкой входных сигналов. Например, одновременная установка единиц на входах триггера в одном случае ни как не влияет на его состояние (это так называемая сохраняющая комбинация), в другом случае, в состоянии 00, приводит к генерации триггера (см. переходы между состояниями 11-00 на графе автомата, представленного рис. 5б в [1]).

Факт режима генерации у триггера малоизвестен. Возможно потому, что его динамическая работа рассматривается очень редко. В большинстве случаев ограничиваются статической картинкой, приводя таблицу соответствия сигналов на выходах триггера сигналам на его входах. Объявляя, как правило, при этом строку таблицы, соответствующую входной комбинации 00, приводящей к комбинации на выходе - 11, запрещенной (для триггера на элементах И-НЕ). Еще реже рассматривается динамика работы триггера при переходе из одного устойчивого состояния в другое. Как при этом ведут себя выходы триггера из таблиц увидеть невозможно. Но это можно, правда, учесть, если построить расширенную таблицу, во входные комбинации которой включить и состояние выходов (см. для примера [2]).

Итак, чтобы триггер вошел в режим генерации, нужно с помощью кнопки с именем "00" установить состояние выходов в 11, а затем кнопкой с именем "11" установить на входах триггера единицы. При выполнении этих действий триггер должен войти в режим генерации. Для модели это обязательно, а реальному триггеру - лишь при выполнении определенных условий (такие условия созданы в мультивибраторе, схема которого аналогична схеме триггера).

Тестирование программы показало, что последовательный триггер входит в режим генерации, а параллельный - может войти, а может и не войти. Параллельный триггер может какое-то количество циклов отработать, а потом остановиться. Его можно опять заставить генерировать, но через какое-то время он остановится обязательно. В чем дело? Если принять за основу, что программа последовательного триггера правильна, то, значит, в параллельной версии не выполняются какие-то условия, приводящие к генерации? А, казалось бы, все правильно, просто, логично, но ...

Состояние выходов триггера в переходном режиме можно увидеть, если замедлить его работу. Для последовательного варианта это сделать сложно, а параллельному триггеру для этого нужно задать время перезапуска процесса его объектам task. Если оно достаточно велико, то можно видеть, что в переходном режиме, как это и должно быть, выходы триггера находятся в единичном состоянии.

"Засечь" визуально состояние выходов последовательного триггера сложно. Это можно, сделать, добавив в проект программный объект, выполняющий эту функции специально. Им может быть еще один объект типа task. И это мы сделаем, но рассматривая уже более детально проблему отслеживания состояний выхода триггера. А пока лишь отметим один интересный факт, связанный с режимом генерации. У последовательного триггера (листинг 1) есть "глупая" строка, устанавливающая состояния выходов триггера в 0 перед их установкой в единицу. Казалось бы, это не имеет смысла, но для отражения реальной ситуации в модели это необходимо. И, что замечательно, объекты Флоры эту ситуацию "отлавливают"! Это видно по "морганию" выходов триггера. В обычной программной среде этого "моргания" не произошло бы (в этом мы еще убедимся), а вот свойства среды Флоры, реализующей близкую к потоковой модели программную модель на такой "фокус" способны.

1.2. От Basic-а к Visual Basic

Программирование по праву считают искусством. Оно столь же разнообразно, как разнообразно любое искусство. В то же время разные языки программирования обладают разной степенью свободы выражения "искусства программирования". У Флоры она одна, у языка Basic - другая, у Visual Basic - третья. По крайней мере, это верно по отношению к их возможностям реализации параллельных процессов. Попробуем проявить свое искусство в реализации триггера сначала на Basic и затем на Visual Basic.

1.2.1. Программа на языке Basic

Почти у каждого программиста есть приемы, позволяющие ему достойным образом выйти из любой ситуации. В "школьном" Basic нет параллельных механизмов, но В.А. Пудов (e-mail: valery@eurocom.ru) предложил свое решение задачи "параллельного" моделирования триггера на языке Basic. Безусловно, эта программа могла бы быть полностью аналогична "последовательной программе" на Flora/C+, представленной на листинге 1 и мы ее еще создадим. Но В.А. Пудов нашел иное решение, в котором его "искусство" проявилось не только в том, что он использовал достаточно известный метод программирования (метод переменной состояния), но и модифицировал его. Это позволило решить поставленную задачу в нетривиальных условиях простейшего процедурного языка. Это еще одно доказательство той простой истины, что искусство владения инструментом важнее возможностей самого инструмента!

Решение В.А. Пудова представлено программой на листинге 3. В ней реализована концепция "теневого состояния". В данном случае реальные состояния моделей хранятся в переменных edQ и edNQ (соответственно прямого и инверсного выходов триггера). Их "тенью" (теневыми состояниями модели) являются значение переменных TstateA и TstateB. Роль их в том, чтобы запомнить новые состояния модели, в то время как она их устанавливает, используя реальные текущие состояния других моделей. Реальные значения состояний устанавливаются по завершении цикла работы программных моделей переносом значений из теневых переменных.

Несмотря на использованный прием, приближающий полученное решение к автоматной модели, его все же нужно отнести к решению на базе БС. Прежде всего, по той причине, что здесь модель БС представлена в явном виде, правда, с активным использованием подпрограмм. При этом структура самой программы усложняется, а сложность отдельных ее компонент уменьшается. Кроме того, такой модульный подход позволяет легко организовать и повторное использование компонент.

Мы не будем непосредственно реализовывать данную программу, а проверим, заложенную в ней концепцию и алгоритм работы, используя для этого более известную программную среду и язык программирования Visual Basic (VB).

1.2.2. RS-триггер на языке Visual Basic

Программа триггера на языке Visual Basic представлена в листинге 4. Внешне она мало похожа на программу из листинга 1, но, тем не менее, она создана на основе все той же идеи "теневых состояний". Весь сервис, связанный с созданием диалога, представлен на листинге 5. Из него видно, что если непрерывная работа программной модели на языке Basic реализована зацикливанием вызова подпрограмм, реализующих модели элементов, то на Visual Basic, это реализовано через процедуру обработки сообщения, посылаемого таймером. Можно было бы создать для каждого элемента свой таймер, но такое решение может быть источником ошибок в работе модели, т.к. реально все элементы функционируют в единой временной шкале, которую проще и надежнее обеспечить одним таймером. Различные таймеры, даже при установке одинакового интервала, не дают гарантии создания единого временного пространства.

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

Для сравнения с данным вариантом реализации триггера в листинге 6 представлен вариант программы с "теневыми состояниями" на Flora/C+.

1.2.3. Тестирование программы

Тестирование программы убеждает в совпадении результатов работы программы на VB с результатами работы программ на Flora/C+. В первую очередь с последовательным вариантом триггера, реализованным во Флоре. В программе на VB, регулируя скорость ее работы, можно видеть состояние выходов триггера во всех режимах его работы. В данном случае наиболее интересно поведение триггера в переходном режиме, т.е. то, что из-за большой скорости переключения последовательного триггера во Флоре, увидеть невозможно.

С точки зрения результатов работы, триггер на Visual Basic может служить эталоном, по которому можно сверять работоспособность других программных реализаций триггера. Он работает, устойчиво выдавая одни и те же результаты в любых режимах работы триггера. И, кроме того, его код и использованная программная модель (БС) полностью соответствуют базовым принципам технологии проектирования современных программных средств и потому могут быть понятны всем программистам.

2. Автоматная модель

Универсальные языки программирования, с точки зрения модели схем программ, поддерживают только одну модель управления, а именно, в форме блок-схем. Реализовать другую модель, используя их, можно или преобразуя их форму ее в эквивалентную форму в виде БС или создать интерпретатор, воспринимающий описание иной модели управления. В отношении автоматной модели управления, первый подход взят за основу в рамках SWITCH-технологии [2], второй - реализован в библиотеке FSA [3]. Но в отличие от SWITCH-технологии FSA использует и несколько иную параллельную автоматную модель, позволяющую просто и, главное, правильно, реализовывать параллельные процессы.

Создание интерпретаторов (сейчас их чаще называют еще виртуальными машинами) достаточно сложная задача, с которой не каждый язык может хорошо справиться. В этом смысле С++ обладает гораздо большими возможностями, чем, например, VB. Именно в силу этих причин принципы интерпретации, реализованные в FSA, сложно реализовать, используя VB. И именно поэтому, применение автоматной модели управления будет рассмотрено только на С++.

Реализация концепции последовательной модели в форме БС и даже в автоматной форме на С++ вряд ли добавит новой информации. Рассматривать последовательную модель триггера в рамках БС для С++ после ее реализации на близком к С++ языке Flora/C+ имеет мало смысла. Это было бы оправдано только для сравнения реализации этой модели в рамках различных языков. Частично мы это уже сделали, реализовав последовательные модели на Flora/C++ на Basic и Visual Basic. Перед нами более важная задача - выбрать модель управления, корректно реализующую параллельные процессы. Во Flore/C+ она была выполнена лишь частично, т.к. параллельная версия триггера, реализованная там, работает весьма ненадежно. Поэтому приступим к реализации параллельной модели триггера, использующей автоматную модель.

2.1. Параллельная модель RS-триггера

Программа на Visual C++ с использованием библиотеки FSA, моделирующая работу триггера, представлена листингами 7, 8, 9 и 10. Особенно по сравнению с параллельной реализацией на Flora/C+ код более объемен. Но это своеобразная плата за гибкость и прозрачности в реализации, за возможность тотального контроля над действиями программы. В листинге 7 представлена программная реализация автоматной модели элемента И-НЕ, граф которой показан на рис. 4с в [1]. Листинг 8 реализует автоматную модель триггера представленную там же на рис. 5а. В данном случае модель элемента И-НЕ - это обычная последовательная автоматная модель. И только модель триггера - параллельная автоматная модель.

В параллельном программировании программировать нужно только последовательные компоненты. Все дальнейшее заключено в специфике запуска параллельных компонент и организации между ними необходимых связей. И то, что делается для реализации триггера во Flore/C+ с применением визуальных средств, на С++ с применением FSA представлено листингом 8.

Листинг 9 представляет программный объект, отвечающий за отображение результатов работы программной модели триггера. Это простейший автомат, выполняющий в цикле отображение информации в окно диалога. Он добавлен для обеспечения автономности и независимости автоматной модели триггера.

В листинге 10 представлен код, выполняющий загрузку автомата отображения в параллельную автоматную среду и установку начальных значений для элемента управления скоростью работы триггера из окна диалога. Этот "ползунок" аналогичен введенному для программы на VB. Только в данном случае управление скорость работы триггера выполняется не с помощью таймера, а через параметры настройки автоматной среды. Кроме этого код класса CStep02View содержит также методы обработчики сообщений от кнопок диалога.

2.2. Тестирование программы

Тестирование параллельной программы на С++ показало полное совпадение конечного и промежуточного результатов с эталоном - программой на VB. С одной стороны, это радует, но с другой - даже простое визуальное сравнение кодов на С++ с кодами на VB показывает насколько на С++ он объемнее. Но, во-первых, в нем большая часть деталей, автоматически переносимых из проекта в проект. Учесть их не составляет большого труда и не требует при этом знаний, связанных с алгоритмом функционированием объекта программирования. Во-вторых, создание кода приложения в последовательном виде на VB может потребовать усилий, перекрывающих простое усвоение правил программирования только что упомянутых нюансов параллельного программирования на С++ с применением FSA.

3. Рекурсивная модель триггера на языке Лисп

В заключение рассмотрим тестирование RS-триггера созданного на языке Лисп (см. листинг 1 в [1]). Сравнивать такую программу с программами, созданными на других языках, сложно. Точнее даже не саму программу, а сам подход к реализации алгоритма. Приемы рекурсивного программирования, используемые в Лисп, возможны и в обычных языках, но сам метод рекурсивного программирования требует своеобразного мышления. С триггером, к счастью, его можно не только понять, но и описать процесс функционирования программы. В данном случае процесс сводится к последовательному рекурсивному вызову модели И-НЕ (функция - nand) для каждого "плеча" триггера.

Эксперимент с программой триггера на Лисп показал, что - увы! - программа не моделирует работу триггера. Она правильно реализует статическую функцию триггера, но, к сожалению, не дает представления о работе триггера в динамике. Хотя по замыслу должна это делать! Хорошо, что мы знаем уже в деталях работу триггера, а иначе, положившись на созданную модель, мы получили бы неверное представление о реальном объекте.

Может быть, потому мы так еще и не решили проблему искусственного интеллекта и вообще потихоньку "спустили на тормоза" объявленные уже давно проекты по созданию ЭВМ пятого поколения, где по замыслу язык Лисп и подобные ему должны были использоваться очень активно? Но, если серьезно, то причины здесь, конечно, не только в этом. Но и в этом тоже!

Воистину - не верь сказанному или всегда подвергай сомнению услышанное! Особенно в случае амбициозных проектов. Так или примерно так можно интерпретировать одно из высказываний небезызвестного Козьмы Пруткова.

Выводы

А эта генерация так уж необходима?
Из переписки.

Параллельное программирование просто обязано быть проще и легче последовательного. Сейчас ситуация обратная. Но на формальном уровне, создавая модель RS-триггера, мы это доказали: параллельные модели проще последовательных. Они состоят из более простых алгоритмически моделей, чем результирующая модель. Параллельная система "сама" реализует алгоритм работы, определяемый алгоритмами своих параллельных компонент и связями между ними. Так, параллельному программисту достаточно знать работу элемента И-НЕ и схему триггера, чтобы создать свою программу. А последовательному программисту нужно знать работу не только элемента И-НЕ, но и в тонкостях разбираться в работе триггера. Ему нужно самому строить результирующую модель и только после этого приступать к ее реализации.

Параллельный программист в полной мере "эксплуатирует" свойство эмергентности сложных систем [4], когда ему нужно знать лишь связи между компонентами, а не алгоритм работы всей системы. "Суммарный алгоритм" своих компонент параллельная среда реализует автоматически. Это чем-то схоже с тем, как последовательный программист использует преимущества непроцедурного программирования в сравнении с процедурным. Чем-то напоминает и то, что, например, a+b равно a*b, кажется, когда a=b = 0 или 2. В остальных случаях, кроме сложения, нужно знать и умножение. Параллельная же среда "умножает" сама.

Создавайте параллельные модели! Во-первых, алгоритмы отдельных компонент проще, а потому и работы меньше, и ошибку найти и локализовать легче! Во-вторых, алгоритм параллельной системы определяется алгоритмами ее компонент и информационными связями между ними. Создать информационные связи не составляет большого труда, а вот создать эквивалентный алгоритм, в чем часто и проявляется искусство программиста, проблема не столь простая: чем больше взаимодействующих компонент - даже простых! - тем сложнее задача. Простая последовательная модель "маленького" триггер - тому подтверждение! Параллельно "играя" (см. [1]), опасайтесь "подделок"! Современные параллельные системы и языки программирования чаще соответствуют уровню 2*2, не давая в остальных случаях гарантию правильного ответа. В этом мы убедились, реализуя модель триггера.

Мы рассмотрели четыре программных модели RS-триггера. Какая из формальных моделей при этом проще и точнее реализует параллелизм из проведенных сравнений, думаю, достаточно понятно. Это модель FSA. Но какую концепцию брать за основу проектирования, этот выбор по-прежнему остается за программистом. Но если его цель не просто писать программы, а писать надежные программы, то выбор, если откровенно, не такой уж большой. Если не сказать - единственный. Если кто-то имеет другое мнение - пусть докажет справедливость и достойность своего выбора созданием, как минимум, правильно работающей моделью триггера. А ведь есть еще и другие параллельные тесты? Еще один тест, созданный на базе только что рассмотренного триггера, мы рассмотрим в третьей части нашей беседы.

Литература

  1. Любченко В.С. Искусство программирования ... RS-триггера?! Ч.1. Электронная схема и формальные модели .
  2. А.А. Шалыто. SWITCH-технология. Алгоритмизация и программирование задач логического управления. - СПб.: Наука, 1998. - 628 с.
  3. Любченко В.С. О бильярде с Microsoft C++ 5.0. <Мир ПК>, № 1/98, с.202-206.
  4. Любченко В.С. Фантазия или программирование? "Мир ПК", №10/97, с.116-119.

Листинг 1. Последовательная модель RS-триггера на Flora/C+.

while (1) {                                    
 if (x1) {                                        
     if (!x2) {                                    
        y1=0; y2=1;                                
        while (x1) {}                                
        y1=1;                                    
     }                                        
     else {                                    
        y1=y2=0;    // введено для режима генерации                
        y1=y2=1;                                    
     }                                        
  }                                        
  else {                                        
     if (x2) {                                    
        y1=1; y2=0;                                
        while (x2) {};                                
        y2=1;                                    
     }                                        
     else {                                    
        y1=y2=1;                                    
     }                                        
  }                                        
}

Листинг 2. Модель логического элемента И-НЕ на Flora/C+

if (x1&&x2) y=0;                                
else y=1;

Листинг 3. Программа RS-триггера на языке Basic

5   s = 0: r = 0: Rem для проверки            
10 GoSub 300: Rem KA_A                
20 GoSub 500: Rem KA_B                
30 stateA = TstateA                    
40 stateB = TstateB                    
50 Print stateA, stateB                    
60   s = 1: r = 1: Rem для проверки            
70 GoTo 10                        
100  Rem предикат вентиля А                
110  ax1 = s And stateB: Return                
200  Rem предикат вентиля Б                
210  bx1 = r And stateA: Return                
300 Rem Автомат А                    
310 If stateA = 1 Then GoTo 360: Rem в состояние 1    
320 Rem состояние 0                    
330 GoSub 100: Rem X1                
340 If ax1 = 0 Then TstateA = 1                
350 Return                        
360 Rem состояние1                    
370 GoSub 100: Rem X1                
380 If ax1 = 1 Then TstateA = 0                
390 Return                        
500 Rem автомат B                    
510 If stateB = 1 Then GoTo 560: Rem в состояние 1    
520 Rem состояние 0                    
530 GoSub 200: Rem X1                
540 If bx1 = 0 Then TstateB = 1                
550 Return                        
560 Rem состояние 1                    
570 GoSub 200: Rem X1                
580 If bx1 = 1 Then TstateB = 0                
590 Return

Листинг 4. Программа RS-триггера на языке Visual Basic

Dim TstateA As Integer                                
Dim TstateB As Integer                                
'   RS-триггер                                    
Public Sub RSTriger()                                
    KA edS, edNQ, edQ, TstateA                            
    KA edR, edQ, edNQ, TstateB                            
    edQ = TstateA                                
    edNQ = TstateB                                
End Sub                                    
'   шаблон: модель элемента И-НЕ триггера                    
Public Sub KA(x1 As Integer, x2 As Integer, y As Integer, Ty As Integer)
    If y = 1 Then                                    
        If (ine(x1, x2) = 1) Then                            
            Ty = 0                                    
        End If                                    
    Else                                        
        If (ine(x1, x2) = 0) Then                            
            Ty = 1                                    
        End If                                    
    End If                                    
End Sub                                    
'   логическая функция двух аргументов - И                    
Private Function ine(x1 As Integer, x2 As Integer) As Integer            
    ine = x1 And x2                                
End Function

Листинг 5. Интерфейс для RS-триггера на языке Visual Basic

'   установка входов триггера в 00                        
Private Sub btClose_Click()                            
    edR = 0: edS = 0                                
End Sub                                    
'   установка входов триггера в 11                        
Private Sub btOpen_Click()                            
    edR = 1: edS = 1                                
    edAccount = 0                                
End Sub                                    
'   инициализация модели                            
Private Sub Form_Load()                            
    Timer1.Interval = 1                                
    edR = 1: edS = 1                                
    hsTimer.Min = 1                                
    hsTimer.Max = 5000                                
    hsTimer.Value = 1                                
End Sub                                    
'   установка периода триггера                            
Private Sub hsTimer_Change()                            
    Timer1.Interval = hsTimer.Value                        
End Sub                                    
'   моделирование дискретного времени                    
Private Sub Timer1_Timer()                            
     RSTriger                                    
End Sub

Листинг 6. Коды объектов Флоры для "теневой" модели RS-триггера

// объект task: задача - модель RS-триггера                    
KA_A();                                    
KA_B();                                    
stateA=TstateA;                                    
stateB=TstateB;                                    
                                        
// predA объект Func : функция - предикат А                    
return (s && stateB);                                
                                        
// predB объект Func :  функция - предикат В                    
return r && stateA;                                
                                        
// KA_A объект Func :  функция - модель первого (А) элемента И-НЕ        
    if (stateA == 1) {                                
        if (predA()==1) {TstateA = 0; }                        
    }                                        
    else {                                    
        if (predA() == 0) {TstateA = 1; }                        
    }                                        
                                        
// KA_B объект Func :  
// функция - модель второго (В) первого элемента И-НЕ    
    if (stateB == 1) {                                
        if (predB()==1) {TstateB = 0; }                        
    }                                        
    else {                                    
        if (predB()==0) {TstateB = 1; }                        
    }

Листинг 7. Модель элемента И-НЕ на С++

class CIne1 : public LFsaAppl                      
{                                
public:                                
    CIne1(bool* x, LFsaAppl** tsk);                
    virtual ~CIne1();                        
protected:                            
    int x3();                            
    int x2();                            
    int x1();                            
    LFsaAppl** tskIne;                    
    bool* bX;                        
};                                
LArc stINE1[] = {                        
        LArc("S1", "S0", "x1x2", "--"),            
        LArc("S0", "S1", "^x1",    "--"),            
        LArc("S0", "S1", "x3",    "--"),            
        LArc()                        
      };                            
CIne1::CIne1(bool* x, LFsaAppl** tsk):LFsaAppl(stINE1)        
{                                
 bX=x; tskIne=tsk;                        
}                                
                                
CIne1::~CIne1() { }                        
                                
int CIne1::x1() { return *bX; }                    
                                
int CIne1::x2() { return string((*tskIne)->FGetState()) == "S1"; }    
                                
int CIne1::x3() { return string((*tskIne)->FGetState()) == "S0"; }

Листинг 8. Программная модель триггера на С++

class CDat;                                
class CFlipFlop                              
{                                    
public:                                    
    char* GetNQ();                            
    char* GetQ();                            
    CFlipFlop(CDat* dat);                        
    virtual ~CFlipFlop();                        
                                    
protected:                                
    CDat* pDatInOu;                        
private:                                    
    LFsaAppl* pIne2;                        
    LFsaAppl* pIne1;                        
    char ch[10];                            
};                                    
CFlipFlop::CFlipFlop(CDat* dat)                        
{                                    
  pIne1= pIne2= NULL;                            
  pIne1= new CIne1(&dat->bX1, &pIne2);                    
  pIne1->FLoad(theApp.pNetFsa,1);                    
  pIne2= new CIne1(&dat->bX2, &pIne1);                    
  pIne2->FLoad(theApp.pNetFsa,1);                    
  pIne3= new CCounter(&pIne1, &pIne2);                    
}                                    
                                    
CFlipFlop::~CFlipFlop()                            
{                                    
  if (pIne1) delete pIne1;                            
  if (pIne2) delete pIne2;                            
}                                    
                                    
char* CFlipFlop::GetQ() { return pIne1->FGetState(); }            
                                    
char* CFlipFlop::GetNQ() { return pIne2->FGetState(); }

Листинг 9. Вывод результатов работы триггера

class CDat;                                
class CPrnt : public LFsaAppl                          
{                                    
public:                                    
    CPrnt(CFormView* pParent, CDat* dat);                
    CPrnt();                            
    virtual ~CPrnt();                            
                                    
protected:                                
    void y1();                            
    CFormView* pOutWnd;                        
    CDat* pCDatOu;                        
};                                    
LArc PRNT[] = {                             
    LArc("S1", "S1", "--","y1"),                     
    LArc()                                 
};                                    
                                    
CPrnt::CPrnt(CFormView* pParent, CDat* dat):LFsaAppl(PRNT)        
{                                    
 pCDatOu=dat; pOutWnd=pParent;                    
}                                    
                                    
CPrnt::CPrnt():LFsaAppl() { }                        
                                    
CPrnt::~CPrnt() { }                            
                                    
void CPrnt::y1()                                
{                                    
 ((CStep02View*)pOutWnd)->m_strQ=                     
     ((CStep02View*)pOutWnd)->GetDocument()->pFF->GetQ();     
 ((CStep02View*)pOutWnd)->m_strNQ=                     
     ((CStep02View*)pOutWnd)->GetDocument()->pFF->GetNQ();     
 ((CStep02View*)pOutWnd)->m_strCnt=                     
     ((CStep02View*)pOutWnd)->GetDocument()->pFF->GetCnt();     
 ((CStep02View*)pOutWnd)->m_strValueCounter=             
     ((CStep02View*)pOutWnd)->GetDocument()->pFF->GetCounter(); 
 pOutWnd->UpdateData(FALSE);                    
}

Листинг 10. Метод класса CStep02View, загружающий объекты моделей схемы

void CStep02View::OnInitialUpdate()                     
{                                    
    CFormView::OnInitialUpdate();                    
 pCPrnt= new CPrnt(this, GetDocument()->pDatInOu); // схема        
 pCPrnt->FLoad(theApp.pNetFsa,1);               // печать        
 theApp.pNetFsa->go_task();                        
                                     
 m_sldTime.SetRange(1, 1000000);    // установка ползунка        
 m_sldTime.SetTicFreq(10);                        
 m_sldTime.SetPos(9900);                        
 theApp.c= m_sldTime.GetPos();                    
 char buffer[20];                                
 m_strTime= ltoa(theApp.c, buffer, 10);                    
 UpdateData(FALSE);                            
                                    
}