вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 25.10.13 23:54
Оценка: 258 (9)
Вышла новая версия библиотеки Nemerle.Statechart, залита в репозитарии Н, должна со следующей сборкой идти вместе с инсталлятором Н, расскажу об изменениях которые произошли. Была проделана большая работа по переписке движка, прочел кучу литературы по автоматам и диаграммам состояний UML, включая стандарт, все функции которые были исправлены или добавлены, привести не удастся, работа за два года, коммитов больше 500, скажу только об основных изменениях. Также для тех кто новичок и ничего не слышал о библиотеке или не знаком с областью конечных автоматов и диаграмм состояний UML я расскажу на примерах в нескольких постах о диаграммах состояний и их представлении в библиотеке и для чего она служит. Если что то упущу или не вспомню, в следующих примерах, и документации опишу последовательно все элементы и конструкции и их использование.

Помимо функций которые уже были и работали, появились новые:



На данный момент реализованы практически все функции из стандарта UML касательно автоматов состояний, остались некоторые редко используемые, но в будущем планируется полностью покрыть стандарт UML. Язык описания DSL и трансформация на этапе компиляции и генерация кода, позволяют сделать на основе описания поддержку любой сложной конструкции и сгенерировать эффективный код, выдавать сообщения об ошибках в случае ошибок в описании языка или семантике анализируемого автомата, такое на данный момент сложно реализуемо на других языках, сравнения по скорости с аналогичными автоматами сделанными на обычных библиотеках, которые строят автомат в рантайме, библиотека по скорости кода обходит как минимум в 7 раз, анализ и выдача ошибок тоже помогают качественному коду автомата. Сам язык автомата, декларативен, содержит лишь информацию касательно описания состояний, переходов событий, действий то есть того как описывается автомат из оригинальной диаграммы и легко перекладывается из диаграммы в код, не требуя усилий в виде сложных описаний в духе метапрограммирования шаблонов C++, или вызова методов для добавления переходов в других языках, с нулевой возможностью выдачи ошибок и очень низкой анализа. Текстовое описание возможно и без диаграммы, при этом создание не сложней чем рисовать диаграмму, никаких дополнительных конструкций кроме необходимой информации не нужно. На данный момент мало или практически нет нормальных генераторов кода для автоматов, поддерживающих все функции UML, потребность в них и заставила заняться этой задачей.

Автоматами по стандарту UML могут снабжаться любые сущности, типа классов, описывая их поведение в четком детерминированном виде, когда все состояния объекта известны и скрыты внутри автомата, внутри каждого состояния объект выполняет какую то задачу (активность), переходы из состояния в другое состояние, наборы и последовательность эффектов при изменениях состояний определяются автоматом или диаграммой, по определенным событиям, при этом класс имеет свой поток управления, реагирует на внешние и внутренние события асинхронно, в каждом состоянии по разному, при этом каждое событие обрабатывается по одному в так называемом шаге до завершения (run-to-completion step) между двумя шагами автомат обрабатывает только одно событие, а после пребывает в стабильном состоянии, исключая порчу или рассинхронизацию своего внутреннего состояния, при работе из нескольких потоков. Все это есть реактивное программирование, когда программа не просто обрабатывает и трансформирует данные, но пребывает в нескольких состояниях, в течении разного времени, могут появляться различные события в любой момент времени, как то нажатие кнопок, временные интервалы, другие сигналы и обстоятельства которые случаются во времени и могут рассматриваться как события, на которые обьект должен реагировать, выполнять последовательность эффектов, изменять свое состояние управляемым способом и реагировать в каждом состоянии на разный набор событий, в таких случаях и стоит применять автоматы состояний и диаграммы, упрощающие их описание, надежность такого приложения возрастает, а отладка упрощается. Все описание поведения приложения, обьекта или какого то процесса может быть описано через состояния, набор эффектов при входах, выходах из состояний и при переходах. Примерами приложений в которых было бы полезно автоматное программирование можно назвать графический интерфейс, где он может пребывать в разных состояниях или режимах и по разному реагировать на действия пользователя, view-model в паттерне MVVM может описываться автоматом. Сама технология WPF может легко использоваться с автоматами, достаточно будет только послать события в автомат, подписаться на какие то действия, и автомат сам будет вызывать действия и эффекты в верной последовательности, все описание поведения может быть сконценрировано в автомате, чтобы изменить поведение приложения достаточно изменить описание или переосмыслить диаграмму. Также автоматы могут применяться в играх, где у обьектов мира бывает множество состояний, неожиданных событий в мире, которые могут появляться когда угодно, на которые они по разному реагируют и должны менять свое состояние, программирование таких сложных процессов через стандартную модель, даже если возможна событийность, без реактивного программирования, четкого описания в диаграммах может привести к трудности при отладке, куче багов и спагетти-кода. Также автоматы могут применяться в workflow процессах, робототехнике, других описаниях автоматных систем, типа разных приборов, имеющих взаимодействие с пользователем типа часов, телефонов, фотоаппаратов, автоматов выдачи валюты и тп. Моделирования обьектов реального мира, которые все изменяют свое состояние, описание протоколов связи и систем реального времени.
Автоматы из библиотеки поддерживают работу со временными интервалами, через временные события, достаточно описать переход из одного состояния в другое через событие after (), можно использовать любые интервалы в секундах, миллисекундах, минутах, часах, днях, автомат сам будет следить за созданием, запуском таймеров, их распределением и оптимизировать чтобы таймеров использовалось по минимуму.

В будущем я думаю сделать генератор не только для Немерле, но и для других языков, включая C/C++ это позволит использовать библиотеку для описания автоматов для систем реального времени и микроконтроллеров, всяких других устройств типа телефонов, планшетов и тп. Также хотелось сделать генератор в диаграммы и наоборот, простой рисовальщик диаграмм, который мог бы генерить код с помощью библиотеки. Также Найтра в будущем будет использоваться как основной генератор парсера, что улучшит текстовый парсинг и обработку ошибок синтаксиса.
В следующих постах опишу на примерах работу с библиотекой, также вы можете сами посмотреть примеры которые есть в папке snippets\Nemerle.Statechart\Tests чтобы начать программировать с помощью библиотеки. Конечно хотелось бы чтобы появились люди которые могли бы потестить библиотеку в своих проектах и приложениях, дать комментарии, предложения и ошибки которые найдут. На английском форуме я также буду вести описание и составлять документацию, если кто то может помочь с переводом на английский тоже бы было неплохо.
Re: вышла библиотека Nemerle.Statechart версия 1.1
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.10.13 19:45
Оценка: +1
Здравствуйте, CodingUnit, Вы писали:

CU> В следующих постах опишу...


Много раз тебе говорил, что статью нужно написать. Посты уйдут вниз, а статья будет всегда доступна и в поиском будет находиться на раз. Ну, а за ней нужно мануал писать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: вышла библиотека Nemerle.Statechart версия 1.1
От: achmed Удмуртия https://www.linkedin.com/in/nail-achmedzhanov-9907188/
Дата: 28.10.13 05:27
Оценка:
Здравствуйте, CodingUnit, Вы писали:

CU>Вышла новая версия библиотеки Nemerle.Statechart, залита в репозитарии Н, должна со следующей сборкой идти вместе с инсталлятором Н, расскажу об изменениях которые произошли. Была проделана большая работа по переписке движка, прочел кучу литературы по автоматам и диаграммам состояний UML, включая стандарт, все функции которые были исправлены или добавлены, привести не удастся, работа за два года, коммитов больше 500, скажу только об основных изменениях. Также для тех кто новичок и ничего не слышал о библиотеке или не знаком с областью конечных автоматов и диаграмм состояний UML я расскажу на примерах в нескольких постах о диаграммах состояний и их представлении в библиотеке и для чего она служит. Если что то упущу или не вспомню, в следующих примерах, и документации опишу последовательно все элементы и конструкции и их использование.


Где посмотреть исходники и примеры не устанавливая nemerle?
Re[2]: вышла библиотека Nemerle.Statechart версия 1.1
От: _NN_ www.nemerleweb.com
Дата: 28.10.13 08:15
Оценка:
Здравствуйте, achmed, Вы писали:

A>Где посмотреть исходники и примеры не устанавливая nemerle?


Исходники: https://github.com/rsdn/nemerle/tree/master/snippets/Nemerle.Statechart
Чтобы поиграться не нужно устанавливать, можно просто бинарники скачать: http://www.nemerle.org/Downloads
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 28.10.13 14:56
Оценка:
Здравствуйте, achmed, Вы писали:

A>Где посмотреть исходники и примеры не устанавливая nemerle?


У библиотеки есть свой репозитарий, https://github.com/CodingUnit/Nemerle.Statechart

там проходит основная работа, и исходники свежее, репозитарий в Н будет периодически синхронизироваться после важных изменений.
Re[2]: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 28.10.13 15:14
Оценка:
Здравствуйте, achmed, Вы писали:

A>Где посмотреть исходники и примеры не устанавливая nemerle?


Примеры находятся в папке Tests\
в StateMachines примеры различных автоматов, на основе них сделаны несколько приложений в папке Tests\
AlarmClock
Calculator
CodePad
GuiFileTest

в папке testsuite негативные и позитивные тесты на все основные функции по названиям например do_activity_and_completion_transition.n

там можно увидеть и весь синтаксис для разных конструкций
Re[2]: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 28.10.13 15:15
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Много раз тебе говорил, что статью нужно написать. Посты уйдут вниз, а статья будет всегда доступна и в поиском будет находиться на раз. Ну, а за ней нужно мануал писать.


Да говорил, я согласен, как будет возможность, обязательно это сделаю.
Re[3]: вышла библиотека Nemerle.Statechart версия 1.1
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.10.13 20:18
Оценка:
Здравствуйте, CodingUnit, Вы писали:

CU>Да говорил, я согласен, как будет возможность, обязательно это сделаю.


Ее уже больше года не находится.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 28.10.13 20:30
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ее уже больше года не находится.


Да я принял решение сначала сделать рабочую библиотеку, предыдущая работоспособность меня не устраивала, сейчас времени больше, хотя несколько важных вещей еще предстоит доделать.
Re: вышла библиотека Nemerle.Statechart версия 1.1
От: _NN_ www.nemerleweb.com
Дата: 28.10.13 21:35
Оценка:
Здравствуйте, CodingUnit, Вы писали:

Хотелось бы конечно сравнения с другими библиотеками.
Чем Statechart выделяется, чем лучше, чем хуже и т.п.

В проекте назревает необходимость построения конечного автомата динамически по конфигурации.
Возможно ли использовать Statechart в таком сценарии ?

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

Ну и вопрос, возможна ли в оптимизация цепочек ?
Примерно если у нас есть такой вариант:

start / this.A == "A" => stateA
start / this.A == "B" => stateB

stateA / this.B == "X" => success
stateB / this.B == "Y" => success


Внутри преобразуется в такой вариант:

start / 
  let a = this.A // кешируем
  if a == "A" => stateAorB (A)
  if a == "B" => stateBorB (B)

stateAorB (arg) /
  let b = this.B // кешируем
  if arg == A
    if b == "X" => success (A)
  if arg == B
    if b == "Y" => success (B)


Если в Statechart есть возможность передавать данные между состояниями, возможно это как раз то что надо.
Надеюсь идея ясна
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 29.10.13 00:34
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Хотелось бы конечно сравнения с другими библиотеками.

_NN>Чем Statechart выделяется, чем лучше, чем хуже и т.п.

Я изучал и использовал несколько библиотек, могу рассказать о них. Есть несколько вариантов, про С++ можно и не говорить, есть две библиотеки boost::statechart и boost::Meta State Machine, обе они на шаблонном метапрограммировании, ну и все вытекающие отсюда последствия, сложность отладки, создания, сообщений об ошибках и использование, в boost::statechart отсутствовала возможность подключать внешние действия к состояниям, приходилось либо создавать все в том же состоянии либо ссылаться на внешние через лямбды или какие то ссылки. Про остальные библиотеки на С++ я не вспоминаю, в основном они предлагают строить автомат вручную на state паттерне и на него что то накладывают, удобства мало. В .net есть несколько библиотек:

Sanford Statemachine Toolkit — библиотека строит конечный автомат, через вызовы методов, можно создать в рантайме, но из за этого невысокая производительность, я строил аналогичные автоматы на своей и на этой, скорость естественно генерируемого кода быстрее в 7 раз, в остальном возможностей у нее немного, неподдерживаются параллельные состояния, defer откладываемые события, временные события (timed events), нет как таковых do активностей в состоянии, сторожевых условий, хотя поддерживаются иерархичные состояния, Junction, Choice псевдосостояния отсутствуют, которые используются для структурирования сложных переходов с действиями и сторожевыми условиями.

QHSM port for .net — порт Quantum Leaps Hierarchical State Machine, использует идеологию активных обьектов, динамически строится автомат, переходы перед запуском перерасчитываются и должны занимать меньше времени на выполнение, есть иерархии событий, дает строить состояния как классы, отсутствует поддержка параллельных состояний, и связанных fork, join псевдосостояний. Junction, Choice псевдосостояния отсутствуют, также нет временных событий (timed events), откладываемых событий (defer). На скорость библиотеку не проверял, мало знаю о ней.

Entropy Simple State Machine — основана на DSL с помощью языка Boo, насколько мне известно очень ограниченная библиотека, но позволяет строить автоматы с помощью простого DSL, все что выше описывал не поддерживается.

Nemerle.Statechart позволяет во первых строит автомат во время компиляции, через простое текстовое описание, все выше описанные конструкции которые есть в UML, но не поддерживаются или поддерживаются плохо, по тем или иным причинам библиотеками, поддерживаются в ней. За счет статического анализа, можно проанализировать правильность описания, отношения между состояними, корректность переходов, определить рабочие активные конфигурации и построить для них код. Переходы расчитываются заранее, и для них генерируется только функция, которая запускается по событию из текущего состояния, в которой есть проверка на сторожевое условие, которое может быть в виде выражения Nemerle, если условие дало true то запускается переход, выполняются действия при выходе, входе при самом переходе, возможно запускается какая то активность, внешний Task который выполняет действия в своем потоке, пока автомат в этом состоянии. События вычисляются из описания, действия, состояния, переходы, обработчики событий все генерируется библиотекой и не требует какого то кодирования, все остается пользователю только описать свои действия которые он хочет чтобы автомат выполнял в состояниях или при переходах, возможно подписаться на какие то действия или определить их методом, при разрешении имен действие привяжется к методу. За счет полноценного анализа на этапе компиляции нет никаких ограничений как обрабатывать автомат, и что генерировать в конце, поэтому поддержать все функции и сложности автоматов UML вполне возможно.

_NN>В проекте назревает необходимость построения конечного автомата динамически по конфигурации.

_NN>Возможно ли использовать Statechart в таком сценарии ?

_NN>Все пытался сформировать вопрос, но как-то не получается внятно.

_NN>Попробую , надеюсь будет понятно.
_NN>Нужно пройтись по дереву , которое задается конфигурацией динамически и при этом сделать разные запросы на каждом уровне.
_NN>Далее в зависимости от успешности запроса есть переход на следующий уровень или цепочка валится.
_NN>Таких цепочек может быть несколько.

_NN>Ну и вопрос, возможна ли в оптимизация цепочек ?

_NN>Примерно если у нас есть такой вариант:

_NN>
_NN>start / this.A == "A" => stateA
_NN>start / this.A == "B" => stateB

_NN>stateA / this.B == "X" => success
_NN>stateB / this.B == "Y" => success
_NN>


_NN>Внутри преобразуется в такой вариант:


_NN>
_NN>start / 
_NN>  let a = this.A // кешируем
_NN>  if a == "A" => stateAorB (A)
_NN>  if a == "B" => stateBorB (B)

_NN>stateAorB (arg) /
_NN>  let b = this.B // кешируем
_NN>  if arg == A
_NN>    if b == "X" => success (A)
_NN>  if arg == B
_NN>    if b == "Y" => success (B)
_NN>


Nemerle.Statechart работает как макрос, есть возможность подавать текст описания и не во время компиляции, но код пока генерировать кроме как с помощью Nemerle не умеет. На класс вешается макрос аттрибут в нем описывается автомат, автомат строится во время компиляции по описанию, во время рантайма изменять структуру автомата нельзя. Я не знаком с областью применения автоматов для разбора регулярных выражений или организации потоков управления, если у вас состояния автомата известны заранее, то можно его построить, динамическая информация для автомата это события, параметры событий, сторожевые условия, и само текущее состояние. Так как автомат вешается на класс и генериться в нем, то поддерживаются действия внутри класса, естественно они имеют доступ к данным всех состояний, могут изменять данные внутри класса, сторожевые условия могут срабатывать или нет на основе каких то переменных, свойств или выражений, если сторожевое условие при переходе, которое проверяется сразу после события перехода, выдает false то переход не будет выполнен. Можно делать более сложные ветки переходов основанные на junction и choice псевдосостояниях, о них я потом расскажу подробнее, можно заглянуть в описание по языку UML. В кратце эти псевдосостояния позволяют сделать переход более сложным, на пути перехода может быть много разных сторожевых условий и действий, дерево разных условий, junction проверяет все условия перед запуском перехода и выбирает единственный путь или отменяет переход если все условия дали false, choice запускается всегда но проверяет сторожевые условия уже когда начался переход, то есть динамически смотрит за сторожевыми условиями, если не нашел нужного то модель неверна, есть возможность использовать else сторожевое условие которое будет выполнятся всегда когда все остальные не сработали. Библиотека может подойти если конфигурация состояний известна заранее, во время компиляции, остальную динамическую информацию можно подать в виде переменных, свойств, данных внутри класса, проверять и изменять их.

_NN>Если в Statechart есть возможность передавать данные между состояниями, возможно это как раз то что надо.

все что можно делать внутри класса в котором определен автомат

_NN>Надеюсь идея ясна

Пока не совсем ясно как строятся эти цепочки как ты показал, если состояния и переходы между ними конкретны и их можно определить на этапе компиляциии, то их можно организовать через события, сторожевые условия, ветвления junction/choice, если состояния и переходы между ними известны только в рантайме, вряд ли здесь чем то может помочь библиотека, вам так или иначе придется самим перестраивать автомат, как здесь может помочь библиотека, каждый раз подавать в нее новое описание чтобы она строилась? Если ты опишешь задачу, как ты хочешь чтобы твои состояния обрабатывались и что и куда генерировалось, то я могу подумать как это можно реализовать. Покажи что в твоем коде является состоянием, есть какие то заранее известные и переходы между ними?
Re[3]: вышла библиотека Nemerle.Statechart версия 1.1
От: _NN_ www.nemerleweb.com
Дата: 29.10.13 12:10
Оценка:
Здравствуйте, CodingUnit, Вы писали:

_NN>>Надеюсь идея ясна

CU>Пока не совсем ясно как строятся эти цепочки как ты показал, если состояния и переходы между ними конкретны и их можно определить на этапе компиляциии, то их можно организовать через события, сторожевые условия, ветвления junction/choice, если состояния и переходы между ними известны только в рантайме, вряд ли здесь чем то может помочь библиотека, вам так или иначе придется самим перестраивать автомат, как здесь может помочь библиотека, каждый раз подавать в нее новое описание чтобы она строилась? Если ты опишешь задачу, как ты хочешь чтобы твои состояния обрабатывались и что и куда генерировалось, то я могу подумать как это можно реализовать. Покажи что в твоем коде является состоянием, есть какие то заранее известные и переходы между ними?

Возьмем такой пример.
Имеем древовидную структуру, скажем XML:

<root name="Root" descripton="Root Desc">
  <child name="Child" description="Child Desc">
    <this name="This" description="This Desc" />
  </child>
</root>


Из элемента 'this' пользователь хочу делать запросы.
К примеру XPath:
1. /parent[@name="Chid"]/parent[@name="Root"]
2. /parent[@description="Child Desc"]/parent[@name="Root"]
3. /parent[@description="Child Desc"]/parent[@description="Root Desc"]

Из этого строится автомат, который пройдется по родителям сделает все запросы на атрибуты и скажет где есть сопоставление.
Мне вернется список того, что сопоставилось.
В данном случае 1,2,3.

Атрибуты и элементы запрашиваются только один раз из документа, а потом сравниваются на разные значения в запросе и решаем идем дальше и для какого из запроса.
Что-то как-то так..
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[4]: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 29.10.13 14:34
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Возьмем такой пример.

_NN>Имеем древовидную структуру, скажем XML:

_NN>
_NN><root name="Root" descripton="Root Desc">
_NN>  <child name="Child" description="Child Desc">
_NN>    <this name="This" description="This Desc" />
_NN>  </child>
_NN></root>
_NN>


_NN>Из элемента 'this' пользователь хочу делать запросы.

_NN>К примеру XPath:
_NN>1. /parent[@name="Chid"]/parent[@name="Root"]
_NN>2. /parent[@description="Child Desc"]/parent[@name="Root"]
_NN>3. /parent[@description="Child Desc"]/parent[@description="Root Desc"]

_NN>Из этого строится автомат, который пройдется по родителям сделает все запросы на атрибуты и скажет где есть сопоставление.

_NN>Мне вернется список того, что сопоставилось.
_NN>В данном случае 1,2,3.

_NN>Атрибуты и элементы запрашиваются только один раз из документа, а потом сравниваются на разные значения в запросе и решаем идем дальше и для какого из запроса.

_NN>Что-то как-то так..

В задачах обработки данных с помощью автоматов я не разбираюсь, и как работают регулярные выражения на них, активные обьекты вам тут не нужны, помоему вам нужен какой то плоский автомат, построенный на дереве, как его строить алгоритм я не представляю, что такое 1, 2, 3 глубина, или что просто есть элемент с таким запросом? Почему это задачу должен решать автомат, автомат не умеет обходить древовидные структуры, это делать должен пользователь на языке или сначала научить автомат это делать. Можно обходить дерево и давать автомату события. Если описание из xml ты преобразуешь в входное дерево библиотеке из макроса, то библиотека сгенерирует нужный автомат. Здесь надо определить что будет событием что состоянием, как будут осуществляться переходы. Как я понимаю событием будет встреченный аттрибут, у меня в автомат можно подавать событие как строку, методом или созданным обьектом, тебе надо будет подать на вход автомату событие, через обработку данных, проходясь по дереву, определить переходы например если видит Child то подает соотв. событие и оно перейдет в конечное состояние в котором в результат будет записано число, как то так, хотя эта задача не нормальная для библиотеки. Если ты сделаешь диаграмму как обработать этот автомат в терминах состояний, событий, переходов, то можно его построить, но вообще разве здесь не проще использовать тот же XPath или какую то обработку ручную, это же задача обработки данных а не реактивного программирования, у вас автомат не живет в состояниях, он сразу же переключается куда то. Вообщем надо сначала сформировать автомат, как он будет выглядеть, генерить автоматы по регулярным выражениям или абстрактной обработке, не задача библиотеки.
Re: вышла библиотека Nemerle.Statechart версия 1.1
От: CodingUnit Россия  
Дата: 02.11.13 02:55
Оценка: 33 (3)
Сегодня я опишу несколько функций и начну с простых примеров, в старых постах я писал на обычных примерах, они давно уехали вниз и я хочу рассказать о новых функциях которых не было раньше.

Начну с простого примера для тех кто не знаком с автоматами и их интерпретацией UML в частности. Опишу простой автомат, он понадобился мне когда надо было распознавать задержку кнопки и выполнять функцию в автомате если кнопка была нажата долгое время, в wpf есть RepeatButton но это совсем не то что мне было нужно и я написал простой автомат чтобы распознавать долгое нажатие кнопки.



Суть его такова, у нас есть два состояния кнопки когда она не нажата и надо определить когда пользователь зажмет кнопку на какой то промежуток времени, с этого времени надо выполнять ускоренное действие, то есть не то которое используется по обычному клику, этот автомат используется в Alarm Clock примере для быстрого ввода времени при настройке, когда надо быстро прокрутить цифры до нужного числа, кликать постоянно сложно, а функцию кнопки можно использовать и для других целей. Итак у нас есть ненажатое состояние NotPressed, когда пользователь нажимает кнопку автомат переходит в следующее состояние проверки Check, если он быстро отжимает кнопку автомат переходит в состояние NotPressed и все возвращается к началу как будто был обычный клик. Если пользователь зажимает кнопку то после 1 секунды срабатывает временное событие и автомат переходит в состояние Задержан (Holded) что означает что клавиша задержана, и можно производить какое то действие если клавиша задержана. Далее если клавиша была отжата, автомат возвращается в начальное состояние и вызывает действие released что клавиша была отжата.
Казалось бы простая задача, но ее решение может быть не столь простым, возиться с таймерами, вводить какие то флаги когда клавиша задержана и производить действия, при том надо будет распознавать когда отжали из состояния когда не была задержана и когда была, все это начало спагетти, а если логика бывает посложней тут и бывает лес флагов, if которые приходится муторно отлаживать и добиваться правильного функционирования, при том никто не гарантирует что ошибка не вскроется потом, а здесь мы имеем тривиальную диаграмму, которая переводится в такой же код на библиотеке и все поведение описывается в пределах диаграммы и терминах автоматов состояний, никакой ошибки быть не должно, поскольку все продумано по наглядной диаграмме изначально.
На диаграмме имеются три состояния (state) в овалах с именами слева направа NotPressed, Check, Holded, состояния бывает простые и композитные, простые не имеют внутренних подсостояний, а композитные имеют, о них расскажу позднее, здесь у нас простые состояния которые достаточны для решения задачи. Начальное состояние выбирается с помощью Начального псевдосостояния (Initial), который обозначен черным кружочком из которого идет переход в состояние, которое будет считаться начальным, с него автомат начинает выполнение. Псевдосостояние это фиктивное ненастоящее состояние, поскольку автомат не может в нем находиться в течении времени и должен сразу перейти из него дальше. Состояния соединены между собой переходами (transition), переходы осуществляются по событию (event), события обозначаются словом рядом с переходом. В стандарт UML помимо простых событий входят временные события (timed events), самое распространенное это after, событие которое отсчитывает время от входа в состояние из которого осуществляется переход, если автомат успевает перейти в другое состояние, временное событие не сработает, в данном случае у нас после 1 секунды сработает событие after и автомат перейдет из Check в Holded. У состояний есть внутренние действия и активности, есть действия которые вызываются при входе в состояние, входные действия (entry), выходные действия (exit), обозначаемые соответствующими ключевыми словами с косой чертой / за которой идет имя действия или выражение на языке, что делает автомат при входе или выходе. В данном случае у нас есть действие holded которое вызывается когда автомат входит в состояние Holded. Входные действия гарантированно вызываются при входе в состояние до всех остальных действий и активностей внутри состояния, поэтому подходят для вызова каких то инициализирующих процедур, перед работой самого состояния, при выходе могут вызываться действия, они вызываются после всех других действий и активностей, и могут использоваться для выполнения очистки ресурсов и других действий при выходе из состояния. В состоянии Holded также есть внутренняя активность (activity), которая обозначается ключевым словом do с косой чертой и именем действия. Активности от действий отличаются тем что могут выполняться долго в течении большого промежутка времени, они выполняются в своем потоке параллельно другим действиям, они начинают работать после входных действий которые выполняются до завершения, то есть синхронно, и автоматически завершаются если состояние перестает быть активным, если активность завершилась то порождается завершающее событие (completion event) и могут выполняться переходы по завершению (completion transition) но об этом позднее. В данном случае у нас просто активность которая выполняется все время пока автомат в состоянии Holded.
Также из состояния Holded идет переход по событию release, когда пользователь отжимает кнопку, к нему также прикреплено действие released, обозначается через косую черту после события прикрепленного к переходу, действия при переходах выполняются после выходных действий состояния из которого выходит переход. Это действие должно вызываться когда у нас произошло отжатие и до этого кнопка была зажата то есть автомат находился в состоянии Holded.
У меня к действиям автомата holded и released прикреплены посылка событий hour_holded и hour_released и такие же для минут, они посылаются в автомат Будильника, где он обрабатывает их дает быстро изменять время.
Из диаграммы переведем автомат в код, этот пример есть в примерах под названием FastKeystroke:


  [statechart(
  <#
  
  state NotPressed
  {
    push => Check;
  }
  
  state Check
  {
    after (1 s) => Holded;
    release => NotPressed;
  }
    
  state Holded
  {
    entry / holded;
    release / released => NotPressed;
    do / doing_while_hold;
  }
    
  #>)]
  public class FastKeystroke
  {
    
    public NeedCancel : bool
    {
      get
      {
        IsInState(StateHolded)
      }
    }
    
  }


Как видите все наглядно, состояния описываются начиная с ключевого слова state Name {} с именем состояния далее в фигурных скобках идет описание тела состояния, переходы, действия и активности. Переход обозначается как событие => состояние; также вписываются действия при переходах событие / действие => состояние;
все остальные элементы описываются аналогично как в UML, entry / holded; входное действие, do / doing_while_hold; активность внутри состояния.
Когда автомат компилируется он при разрешении имен действий и активностей наблюдает за методами, если есть метод с таким именем, автомат использует его и привязывает к соотв. действию, если метода с таким именем не нашлось библиотека генерирует для него событие, которое запускается когда выполняется соотв. действие внутри автомата, к действию можно позднее прикрепиться из внешних классов. В данном случае я оставил действие holded на него идет подписка извне, также и для активности, для них можно создать методы внутри класса при этом активность будет иметь такую сигнатуру:

doing_while_hold(tok : CancellationToken) : void
{
 while (!tok.IsCancellationRequested)
 {
 }
}

в качестве параметра передается CancellationToken он нужен для автоматического завершения активности, если состояние перестанет быть активным и в методе надо обрабатывать флаг IsCancellationRequested, сами же do активности реализованы с помощью Task ов, облегченный вариант потоков, они идеально подходят для активностей. Также я добавил свойство IsCancel с методом IsInState(StateHolded), оно нужно для того чтобы знать что нужно отменить событие click при отжатии, чтобы у кнопки не сработал обычный клик, обрабатывается уже в wpf, IsInState это метод который генерируется для каждого автомата, проверяет что автомат находится в соотв.состоянии, StateHolded это публичное статичное поле, в котором лежит экземпляр класса состояния, к ним можно обращаться извне, IsInState используется также в сторожевых условиях, но об этом позднее.

Я хочу провести некоторый рефакторинг автомата, у автомата наблюдается некоторая повторяемость, которую можно упростить переход по событию release идет как из Check так и из Holded почему бы нам не обобщить их, назовем общее состояние для них обоих как Pressed, и из него один переход release:



Теперь у нас иерархичный автомат, появилось состояние Pressed у которого два подсостояния Check и Holded, при этом Check начальное состояние Pressed в него он переходит сразу после события push. Когда активно внутреннее состояние, активно и родительское состояние, иерархичные автоматы это разновидность подхода придуманного Давидом Харелом и позднее обобщенного в стандарте UML, они позволяют описывать более сложное поведение и структурировать автомат, при этом соблюдается LSP Liskov Substitution Principle, внутреннее состояние обладает чертами родителя и реагирует на события родителя, в данном случае событие release запускает переход как из Check так и из Holded, два перехода мы описали через один, иерархичные состояния позволяют и обобщить повторяющиеся конструкции, что упрощает дизайн автомата. Я убрал действие из перехода по событию release в выходное действие Holded. Описание автомата в библиотеке осталось таким же декларативным:



[statechart(
  <#
  
  state NotPressed
  {
    push => Check;
  }
  
  state Pressed
  {
    release => NotPressed;

    state Check
    {
      after (1 s) => Holded;
    }
    
    state Holded
    {
      entry / holded;
      exit / released;
      do / doing_while_hold;
    }
  }
  #>)]
  public class FastKeystroke
  {
    
    public NeedCancel : bool
    {
      get
      {
        IsInState(StateHolded)
      }
    }
    
  }


появляется состояние Pressed, в которое вкладываются подсостояния Check и Holded. В родительском остается переход release => NotPressed;
Также возможен синтаксис : Parent этот синтаксис применяется когда большая глубина вложенности и позволяет упростить описание до такого:


  state Pressed
  {
    release => NotPressed;
  }

  state Check : Pressed
  {
    after (1 s) => Holded;
  }
    
  state Holded : Pressed
  {
    entry / holded;
    exit / released;
    do / doing_while_hold;
  }


здесь появляется описание родительского состояния Pressed через : после имени состояния, такое описание похоже на описания классов и будут понятны всем кто пишет в стиле ООП.
После компиялции автомата, будет сгенерирован класс содержащий все обьявления необходимые для его работы. Автомат после создания через конструктор необходимо запустить через метод Initiate() это переводит автомат в начальное состояние, далее события можно посылать через специально сгенерированные методы по именам событий например fsm.push(); fsm.release(); их можно прикрепить к кнопке для событий например PreviewMouseDown и PreviewMouseUp в WPF, также привязаться к событиям действий holded, released чтобы выполнять действия при начале когда кнопка задержана и при отпуске. Я использовал для них посылку событий в другой автомат Будильник. Также можно запускать события через метод PostEvent(), в него подается экземпляр Event, базового класса для событий внутри этого автомата, он генерируется внутри и его экземпляры можно создать, бывают события с параметрами о них расскажу позднее. Для временных событий генерируется таймер System.Timer он стартует при входе в состояние и останавливается при выходе, при переполнении запускает метод события. Методы событий складывают его экземпляр в пул событий который выполнен на ConcurrentQueue и сразу же выходят, пул обрабатывает события в отдельном потоке по одному, для обеспечения run-to-completion step, в него можно посылать события из любых потоков, пока автомат не выполнит одного события до завершения включая все действия при переходе в другое, он не может выполнять и реагировать на другие события.
Ну вот и все что я хотел рассказать на сегодня, если что то непонятно пишите. В данном посте рассказал о простых автоматах, состояниях, переходах, действиях при входе, выходе, активностях, об этом я и раньше рассказывал, новыми были временные события, : parent синтаксис и реализация активностей. Дальше расскажу об остальных функциях.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.