Static Finite State Machine
От: Аноним Alexander Nikolayenko  
Дата: 08.10.05 06:13
Оценка: 911 (38)
Статья:
Static Finite State Machine
Автор(ы): Alexander Nikolayenko
Дата: 08.10.2005
Машина с конечным числом состояний (FSM, Finite State Machine, или как принято называть по-русски, конечный автомат, КА) представляет собой одну из наиболее полезных концепций в арсенале разработчика. Существует несколько методик реализации конечных автоматов, но, забегая вперед, хочется сказать, что достойный результат дают только те из них, которые связаны с генерацией кода. Возможности, предоставляемые последней версией стандарта C++ и реализованные в последних версиях компиляторов, позволяют генерировать код во время компиляции основного кода проекта. Это дает возможность избежать использования отдельных утилит или расширений IDE и, оставаясь в рамках единого языка (C++), создавать приемлемые для практического использования реализации КА, которые при этом легко поддерживать и развивать.


Авторы:
Alexander Nikolayenko

Аннотация:
Машина с конечным числом состояний (FSM, Finite State Machine, или как принято называть по-русски, конечный автомат, КА) представляет собой одну из наиболее полезных концепций в арсенале разработчика. Существует несколько методик реализации конечных автоматов, но, забегая вперед, хочется сказать, что достойный результат дают только те из них, которые связаны с генерацией кода. Возможности, предоставляемые последней версией стандарта C++ и реализованные в последних версиях компиляторов, позволяют генерировать код во время компиляции основного кода проекта. Это дает возможность избежать использования отдельных утилит или расширений IDE и, оставаясь в рамках единого языка (C++), создавать приемлемые для практического использования реализации КА, которые при этом легко поддерживать и развивать.
Re: Static Finite State Machine
От: _FRED_ Черногория
Дата: 28.10.05 07:10
Оценка: 6 (1)
Здравствуйте, Alexander Nikolayenko, Вы писали:

AN>Статья:

AN>Static Finite State Machine
Автор(ы): Alexander Nikolayenko
Дата: 08.10.2005
Машина с конечным числом состояний (FSM, Finite State Machine, или как принято называть по-русски, конечный автомат, КА) представляет собой одну из наиболее полезных концепций в арсенале разработчика. Существует несколько методик реализации конечных автоматов, но, забегая вперед, хочется сказать, что достойный результат дают только те из них, которые связаны с генерацией кода. Возможности, предоставляемые последней версией стандарта C++ и реализованные в последних версиях компиляторов, позволяют генерировать код во время компиляции основного кода проекта. Это дает возможность избежать использования отдельных утилит или расширений IDE и, оставаясь в рамках единого языка (C++), создавать приемлемые для практического использования реализации КА, которые при этом легко поддерживать и развивать.


AN>Авторы:

AN>Alexander Nikolayenko

В статье встретился:

ПРИМЕЧАНИЕ
В общем случае, ситуация, когда разные события являются источником перехода с вызовом одного и того же действия, не является ошибкой (крайнее справа изображение на рисунке 3).

Но "крайнее справа изображение на рисунке 3", если я верно разглядел , показывает переход по одному событию в различные сосояния. Это просто опечатка, или я что-то не понял?

За статью спасибо : легко читается; многие, напрямую не связанные с тематикой, вопросы понятно объяснены (например, "Реализация шаблона MultipleInheritance"), так что по прочтении "тёмных пятен" не остаётся

Разве что вот, в самом начале, про генератора КА:

В статье генератор диагностирует неполноту спецификации. Это я понимаю. А какими механизмами, в общих чертах, он мог бы её дополнить
<< RSDN@Home 1.2.0 alpha rev. 616 >> =11:10= [Windows 2003 — 5.2.3790.65536]
under «*none*»
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Static Finite State Machine
От: Аноним  
Дата: 28.10.05 14:59
Оценка:
Здравствуйте, _FRED_, Вы писали:


_FR>Но "крайнее справа изображение на рисунке 3", если я верно разглядел , показывает переход по одному событию в различные сосояния. Это просто опечатка, или я что-то не понял?

Возможно опечатка. К сожалению, последнюю редакторскую правку я не видел и собственно печатной версии у меня тоже нет.

_FR>
  • Поскольку спецификация может быть неполной, генератор может дополнить ее.
    Будем считать — это стилистическое упущение. Под спецификацией здесь имелось ввиду не только TransitionTable, а полный набор входных параметров шаблона Builder. Таким образом если клиент не указывает тип Монитора то генератор применяет умолчательную для него версию.

    _FR>За статью спасибо : легко читается; многие, напрямую не связанные с тематикой, вопросы понятно объяснены (например, "Реализация шаблона MultipleInheritance"), так что по прочтении "тёмных пятен" не остаётся

    Спасибо. По правде говоря, еще остались опасения, что она получилась "тяжеловатой"
  • Re: Static Finite State Machine
    От: Klirik  
    Дата: 17.11.05 14:02
    Оценка:
    AN>Статья:
    AN>Static Finite State Machine
    Автор(ы): Alexander Nikolayenko
    Дата: 08.10.2005
    Машина с конечным числом состояний (FSM, Finite State Machine, или как принято называть по-русски, конечный автомат, КА) представляет собой одну из наиболее полезных концепций в арсенале разработчика. Существует несколько методик реализации конечных автоматов, но, забегая вперед, хочется сказать, что достойный результат дают только те из них, которые связаны с генерацией кода. Возможности, предоставляемые последней версией стандарта C++ и реализованные в последних версиях компиляторов, позволяют генерировать код во время компиляции основного кода проекта. Это дает возможность избежать использования отдельных утилит или расширений IDE и, оставаясь в рамках единого языка (C++), создавать приемлемые для практического использования реализации КА, которые при этом легко поддерживать и развивать.


    1. Выглядит неплохо. Однако, попытавшись "попробовать" обнаружил массу ссылок на исходники, однако вот самих исходников, увы, не нашёл. Где же они?

    Например, в разделе "КА как конкретный тип, созданный на основе STT" — ..."Эту задачу выполняет шаблонный класс ContextEnumeratorT (реализацию этого шаблона смотрите в исходном коде — fsm.h)". Где искать этот хедер? На диске, прилагаемом к журналу — увы, не нашёл. Может, плохо искал?

    2. Заметил опечатку (куда же без них...). Например, сразу после листинга 7 встречается некий "TAPELIST_4" (вместо "TYPELIST_4). Вряд ли вспомню о ней — но было бы неплохо, если бы кто-нибудь отрапортовал о ней через орфус, когда статья будет выложена на сайте.
    Re: Static Finite State Machine
    От: Andrew S Россия http://alchemy-lab.com
    Дата: 03.02.06 09:30
    Оценка: 1 (1)
    Хорошая статья, спасибо
    Несколько замечаний к тому, что выложено на rsdn:
    1. Поскольку логика переходов распределена (отсутствует какое-либо одно место, где можно посмотреть всю конструкцию в целом, как на рисунке Error! Reference source not found.)
    2. Шаблоны C++ являются полный по Тьюрингу подъязык C++ периода компиляции.
    3. На рисунке Ошибка! Источник ссылки не найден. представлены наиболее распространенные ошибки при проектировании детерминированного КА (ДКА).
    4. У всех "сворачиваемых" примеров кода вместо рисунка крестика слева от названия выводится пустая рамка "рисунок не найден" (http://rsdn.ru/images/ls2.gif).
    http://www.rusyaz.ru/pr — стараемся писАть по-русски
    Re: Static Finite State Machine
    От: pullover  
    Дата: 05.02.06 14:10
    Оценка:
    спасибо за статью, жаль что примеры в VS2005 приводят к падению компилятора.
    Может какие дополнительные опции задать надо?
    Re: Static Finite State Machine
    От: pintelou  
    Дата: 06.02.06 18:57
    Оценка:
    Очень элегантная реализация. Мне только не вполне очевидно, насколько сложно (и вообще — насколько реально) добавить поддержку нескольких одновременно активных состояний. Это важно для обработки fork/join псевдосостояний.
    Re: Static Finite State Machine
    От: ioni Россия  
    Дата: 08.02.06 10:05
    Оценка:
    Здравствуйте, Alexander Nikolayenko, Вы писали:

    Статья хорошая
    но при попытке попользоваться в vs2005 вызывало падение компилятора
    поправил макросы

    #define FSM_TRANSITION(BaseState, Event, TargetState, ContextClass, ContextMethod)   \
        fsm::TransitionT<ContextClass>::DefineT< BaseState, Event, TargetState, &ContextClass::ContextMethod>
    
    #define FSM_TRANSITION_NO_ACTION(BaseState,Event,TargetState)   \
        fsm::TransitionT<fsm::DefaultContext>::DefineT<BaseState,Event,TargetState, &fsm::DefaultContext::doNothing>


    и вроде тестовые примеры собрались

    но при попытке создать свою машину состояний (семь состояний, размер таблицы переходов двадцать )
    экземпляр машины был создан успешно, а вот при попытке вызвать функцию postEvent получил снова internal compiler error
    думаю что на этом мое терпение закончиться
    Re[2]: Static Finite State Machine
    От: Аноним  
    Дата: 08.02.06 16:18
    Оценка:
    Здравствуйте, ioni, Вы писали:

    I>но при попытке создать свою машину состояний (семь состояний, размер таблицы переходов двадцать )

    I>экземпляр машины был создан успешно, а вот при попытке вызвать функцию postEvent получил снова internal compiler error
    I>думаю что на этом мое терпение закончиться

    Предположение на вскидку — разные версии MSVC. Собственно (и к сожалению) эти опасения высказывались в разделе "переносимость".
    С точки зрения Стандарта, Ваши изменения (добавление & к имени функции члена) эквивалентны первоночальным, однако, по тем или иным причинам, "помагают" компилятору не запутаться на этапе синтаксического разбора. Возможно, ему необходима "помощь" и при раскрутке postEvent. (я не думаю что 20 — это число превышающее внутренее ограничение компилятора для инстанциирования рекувсивных шаблонов)

    Если Ваше терпение все еще не закончилось (я, кстати на это надеюсь), отошлите Ваш проэтк на мой адрес. Для начала необходимо убедиться что это действительно проблема связана с новой версией. (Я пока использую 13.10.3077). и если это предположение окажется верным прийдется "подчистить" наиболее запутанные конструкции, в рамках Стандарта конечно.

    Спасибо
    Re[2]: Static Finite State Machine
    От: Adopt  
    Дата: 08.02.06 22:20
    Оценка:
    Здравствуйте, pintelou, Вы писали:

    P>Очень элегантная реализация. Мне только не вполне очевидно, насколько сложно (и вообще — насколько реально) добавить поддержку нескольких одновременно активных состояний. Это важно для обработки fork/join псевдосостояний.


    можно поподробней?
    ... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
    Re[3]: Static Finite State Machine
    От: Andrew S Россия http://alchemy-lab.com
    Дата: 09.02.06 01:16
    Оценка:
    А>С точки зрения Стандарта, Ваши изменения (добавление & к имени функции члена) эквивалентны первоночальным, однако, по тем или иным причинам, "помагают" компилятору не запутаться на этапе синтаксического разбора.

    Так, для справки. Не ставить амперсанд для взятия адреса метода — это несоответствие стандарту. То, что некоторые компиляторы лояльно относятся к этому, вас не оправдывает
    PS для адреса функции можно и не ставить, оставлено для обратной совместимости с С.
    http://www.rusyaz.ru/pr — стараемся писАть по-русски
    Re[3]: Static Finite State Machine
    От: ioni Россия  
    Дата: 09.02.06 10:34
    Оценка:
    Здравствуйте, Аноним, Вы писали:

    А>Здравствуйте, ioni, Вы писали:


    А>Если Ваше терпение все еще не закончилось (я, кстати на это надеюсь), отошлите Ваш проэтк на мой адрес. Для начала необходимо убедиться что это действительно проблема связана с новой версией. (Я пока использую 13.10.3077). и если это предположение окажется верным прийдется "подчистить" наиболее запутанные конструкции, в рамках Стандарта конечно.


    значит раасказываю
    если сделать проект с нуля (обычное консольное) и описать собственные состояния с объявлением собственного класса то получаю internal compiler error
    если же взять ваше приложение (к примеру turnstile/main.cpp) и на его основе описать то же самое то все нормальное и собирается и работает
    свой пример я посылаю на ваш адрес
    Re[3]: Static Finite State Machine
    От: pintelou  
    Дата: 09.02.06 15:45
    Оценка:
    P>>Очень элегантная реализация. Мне только не вполне очевидно, насколько сложно (и вообще — насколько реально) добавить поддержку нескольких одновременно активных состояний. Это важно для обработки fork/join псевдосостояний.

    A>можно поподробней?


    После прохождения псевдосостояния fork в автомате появляется несколько одновременно активных состояний. Они реагируют на внешние события независимо друг от друга. Чтобы не возникало путаницы, существует требование, что конкурентные активные состояния могут существовать только в т.н. ортогональных регионах, т.е. грубо говоря их пути не должны пересекаться. В узле join активные состояния наоборот, объединяются.
    Такое поведение описано в стандарте UML и, вообще говоря, для работы со сложными автоматами его надо бы уметь поддерживать. Еще одна совершенно незаменимая вещь — submachine state. Что это такое? Нарисовали автомат, запрограммировали, отладили. Теперь при помощи submachine на него можно ссылаться из других автоматов и использовать как готовый компонент.
    Re[4]: Static Finite State Machine
    От: Аноним  
    Дата: 09.02.06 16:26
    Оценка:
    Здравствуйте, Andrew S, Вы писали:

    AS>Так, для справки. Не ставить амперсанд для взятия адреса метода — это несоответствие стандарту. То, что некоторые компиляторы лояльно относятся к этому, вас не оправдывает

    AS>PS для адреса функции можно и не ставить, оставлено для обратной совместимости с С.

    Hmm... Спасибо. Век живи, век учись ...
    Re[4]: Static Finite State Machine
    От: Аноним  
    Дата: 10.02.06 15:00
    Оценка:
    Здравствуйте, ioni, Вы писали:

    I>значит раасказываю

    I>если сделать проект с нуля (обычное консольное) и описать собственные состояния с объявлением собственного класса то получаю internal compiler error
    I>если же взять ваше приложение (к примеру turnstile/main.cpp) и на его основе описать то же самое то все нормальное и собирается и работает
    I>свой пример я посылаю на ваш адрес

    Теперь моя очередь
    Что бы открыть ваш проэкт на моем "старом" окружении пришлось подправить в TestJmSt.vcproj
    ProjectType="Visual C++"
    Version="7.10" // <--- было Version="8.00"
    и ... он собрался успешно (Debug configuration)!
    В период прогона, в консоле образовался следующий аутпут

    >>SM current state: IDLE

    >>
    >>action--->AssignJob()
    >>SM current state: READY

    Я так понимаю — это ожидаемый результат?!

    В Release configuration проэкт НЕ собрался но по другой причине
    >>cl : Command line error D2016 : '/GL' and '/YXstdafx.h' command-line options are incompatible
    Я отключил использование precompiled headers.
    и получил опять работающую программу.

    Давайте сравним еще раз установки компилятора вот мои

    /O2 /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /FD /EHsc /MD /GS /YX"stdafx.h" /Fp"Release/TestJmSt.pch" /Fo"Release/" /Fd"Release/vc70.pdb" /W3 /nologo /c /Wp64 /Zi /TP
    Re[5]: Static Finite State Machine
    От: ioni Россия  
    Дата: 12.02.06 14:46
    Оценка:
    Здравствуйте, Аноним, Вы писали:

    А>Давайте сравним еще раз установки компилятора вот мои

    А>/O2 /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /FD /EHsc /MD /GS /YX"stdafx.h" /Fp"Release/TestJmSt.pch" /Fo"Release/" /Fd"Release/vc70.pdb" /W3 /nologo /c /Wp64 /Zi /TP


    видимо это приколы новой версии компилятора
    так что даже и не знаю....
    впрочем спасибо за участие
    Re[4]: Static Finite State Machine
    От: Аноним  
    Дата: 15.02.06 21:48
    Оценка:
    Здравствуйте, pintelou, Вы писали:

    P>Еще одна совершенно незаменимая вещь — submachine state. Что это такое? Нарисовали автомат, запрограммировали, отладили.

    P>Теперь при помощи submachine на него можно ссылаться из других автоматов и использовать как готовый компонент.

    А не проще сделать иерархию подстостояний в виде дерева ? Во всяком случае у меня такое (пока криво) но работает

    Из статьи :

    >Проблема этого подхода в том, что добавление нового события приведет к модификации существующего кода.

    >Мы будем вынуждены добавить новую функцию-член в класс State и, как следствие, модифицировать все его производные модули

    Я в данном случае применил декларативный метод описания состояния и событие перехода в него.


    Консольный примерчик :
                IStateTransition initTransition            =    new InitStateTransition("init:VIEWING");
                IStateTransition viewingTransition        =    new StateTransitionBase("benutzereingabe:CHANGING;del:DELETING;finalize:FINAL");
                IStateTransition changingTransition        =    new StateTransitionBase("ok:VALIDATING;cancel:CANCELED");
                IStateTransition canceledTransition        =    new StateTransitionBase("view:VIEWING");
                IStateTransition validatingTransition    =    new StateTransitionBase("validation_ok:VIEWING;validation_error:CHANGING");
                IStateTransition deletingTransition        =    new StateTransitionBase("del_ok:VIEWING;del_error:VIEWING");
    
                IState initState        = new InitState(initTransition);
                IState viewingState        = new StateBase("VIEWING"    ,viewingTransition);
                IState changingState    = new StateBase("CHANGING"    ,changingTransition);
                IState canceledState    = new StateBase("CANCELED"    ,canceledTransition);
                IState validatingState    = new StateBase("VALIDATING",validatingTransition);
                IState deletingState    = new StateBase("DELETING"    ,deletingTransition);
                IState finalState        = new FinalState();
    
                IState state1            =    new StateBase("EDIT");
    
                state1.AddSubstate(initState);
                state1.AddSubstate(viewingState);
                state1.AddSubstate(changingState);
                state1.AddSubstate(canceledState);
                state1.AddSubstate(validatingState);
                state1.AddSubstate(deletingState);
                state1.AddSubstate(finalState);
    
                new StateControllerBase(state1);
    
                Console.WriteLine(state1);
                Console.WriteLine("{0}.HasSubStates == {1}",state1.StateName,state1.HasSubStates);
                Console.WriteLine("{0}.HasController == {1}",state1.StateName,state1.HasController);
                Console.WriteLine("{0}.HasTransition == {1}",state1.StateName,state1.HasTransition);
    
                for(int substate_index = 0; substate_index < state1.SubStates.Count;substate_index++)
                {
                    Console.WriteLine("{0}.[{1}].{2}",state1.StateName,state1.SubStates[substate_index].StateName,state1.SubStates[substate_index].Transition);
                }
                Console.WriteLine();
    
                IStateEvent ev = null;
    
                while(true)
                {
    
                    Console.WriteLine("CURRENT {0}.[{1}].{2}",state1.StateName,state1.Controller.CurrentState.StateName,state1.Controller.CurrentState.Transition);
                    Console.WriteLine();
                    Console.Write("event : ");
                    
                    string eventName = Console.ReadLine().Trim();
                    
                    if (eventName== "exit") break;
    
                    ev = new StateEventBase(eventName);
                    Console.WriteLine("--- We send [{0}] event ---",ev.EventName);
                    state1.Controller.OnStateEvent(ev);
                    Console.WriteLine();
                }


    Вывод на консоль :
    StateName=EDIT,SubStates={StateName=INIT,StateName=VIEWING,StateName=CHANGING,StateName=CANCELED,StateName=VALIDATING,StateName=DE
    LETING,StateName=FINAL}
    EDIT.HasSubStates == True
    EDIT.HasController == True
    EDIT.HasTransition == False
    EDIT.[INIT].Transitions = {init:VIEWING}
    EDIT.[VIEWING].Transitions = {benutzereingabe:CHANGING;finalize:FINAL;del:DELETING}
    EDIT.[CHANGING].Transitions = {ok:VALIDATING;cancel:CANCELED}
    EDIT.[CANCELED].Transitions = {view:VIEWING}
    EDIT.[VALIDATING].Transitions = {validation_ok:VIEWING;validation_error:CHANGING}
    EDIT.[DELETING].Transitions = {del_error:VIEWING;del_ok:VIEWING}
    EDIT.[FINAL].Transitions = {finalize:FINAL}
    
    CURRENT EDIT.[VIEWING].Transitions = {benutzereingabe:CHANGING;finalize:FINAL;del:DELETING}
    
    event : benutzereingabe
    --- We send [benutzereingabe] event ---
    
    CURRENT EDIT.[CHANGING].Transitions = {ok:VALIDATING;cancel:CANCELED}
    
    event : canc
    --- We send [canc] event ---
    
    CURRENT EDIT.[CHANGING].Transitions = {ok:VALIDATING;cancel:CANCELED}
    
    event : cancel
    --- We send [cancel] event ---
    
    CURRENT EDIT.[CANCELED].Transitions = {view:VIEWING}
    
    event :
    Re[5]: Static Finite State Machine
    От: pintelou  
    Дата: 16.02.06 15:37
    Оценка:
    P>>Еще одна совершенно незаменимая вещь — submachine state. Что это такое? Нарисовали автомат, запрограммировали, отладили.
    P>>Теперь при помощи submachine на него можно ссылаться из других автоматов и использовать как готовый компонент.

    А>А не проще сделать иерархию подстостояний в виде дерева ? Во всяком случае у меня такое (пока криво) но работает

    Это композитные состояния — собственно на них-то все и построено. Но у нас код частично генерировался по UML диаграммам, поэтому реюзабл компоненты мы рисовали на отдельных диаграммах и ссылались на них как на submachine state (кстати, ведь в одном автомате можно использовать компонент более одного раза). А в коде генерировались естественно composite states — как в вашем примере.

    А>Из статьи :


    >>Проблема этого подхода в том, что добавление нового события приведет к модификации существующего кода.

    >>Мы будем вынуждены добавить новую функцию-член в класс State и, как следствие, модифицировать все его производные модули

    А>Я в данном случае применил декларативный метод описания состояния и событие перехода в него.

    Аналогичный подход использовался и у нас, только к переходам еще можно цеплять actions и guard conditions (без guard conditions — вообще мало чего можно сделать). Решение получается более кондовое, чем с использованием метапрограммирования, но по-моему более приспособленное для использования в боевых условиях. Во-первых, за счет большей гибкости (как мне кажется), а во-вторых, код получается намного более простым для понимания и менее требовательным к компиляторам.
    Re[5]: Static Finite State Machine
    От: Аноним  
    Дата: 16.02.06 17:13
    Оценка:
    Здравствуйте, Аноним, Вы писали:


    А>Я в данном случае применил декларативный метод описания состояния и событие перехода в него.


    Из статьи: "Проверка на непротиворечивость КА во время компиляции имеет одно явное преимущество перед динамической версией (листинг 3) – возможность обнаружения ошибок на стадии компиляции."
    Насколько я понял в этом и заключается "фишка" этого решения. При вашем подходе вся нагрузка ложится на ран-тайм и обработка ошибочных ситуаций зависит от совести разработчика, написавшего алгоритм интерпретации переходов времени выполнения. К примеру, вы идентифицируете ваши состояния используя строки как то "VALIDATING", однако в силу тех или иных причин в строке перехода написано следующее "ok:VALIDETING;cancel:CANCELED" (A <---> E). Ошибка в написании одной буквы может привести к самым плачевным результатам, если через некоторое время она будет обнаружена на стороне "жирного" заказчика и притом в самый не подходящий момент как для него так и для вас.
    В статье предлагается использовать события, состояния и переходы как C++ типы, тем самым ваш спокойный сон будет зависеть от совести компилятора. Вы не сможете перейти в состояние, которого нет или вызвать какую либо активность если она не присутствует в виде типа (функция-член контекста) и т.д.

    Гибкость и надежность, к сожалению, две вещи практически противоположные. К примеру ваше решение можно сделать еще гибче, если всю декларацию вынести из исходного кода во внешний текстовый файл (а ля XML), добавить парсер и вас больше никогда не будет беспокоить перекомпиляция если спецификация машины изменится. Однако, интуитивно, мы можем предположить что в нем будет больше ошибок (по сравнению с оригинальной версией) и вероятность "все, приехали" возрастает.
    Ваше решение — гибче, решение в статье — более надежно. И только в конкректной ситуации мы можем сказать что для нас более значимо.
    Re[6]: Static Finite State Machine
    От: Аноним  
    Дата: 17.02.06 10:08
    Оценка:
    Здравствуйте, pintelou, Вы писали:

    А>>Я в данном случае применил декларативный метод описания состояния и событие перехода в него.

    P>Аналогичный подход использовался и у нас, только к переходам еще можно цеплять actions и guard conditions (без guard conditions — вообще мало чего можно сделать). Решение получается более кондовое, чем с использованием метапрограммирования, но по-моему более приспособленное для использования в боевых условиях. Во-первых, за счет большей гибкости (как мне кажется), а во-вторых, код получается намного более простым для понимания и менее требовательным к компиляторам.

    У меня всё это можно реализовать посредством реализацией интерфейса IState

    
    public interface IStateEventHandler
    {
        bool OnStateEvent(IStateEvent Event);
    }
    
    public interface IState : IStateEventHandler
    {
        IState                Owner                    {get;set;}
        IStateController    Controller                {get;set;}
        IStateTransition    Transition                {get;}
        string            StateName                {get;}
        bool            HasOwner                {get;}
        bool            HasSubStates            {get;}
        bool            HasController            {get;}
        bool            HasTransition            {get;}
        bool            HasStateEventListener    {get;}
    
        bool            CanChange                {get;set;}
        IStateContainer        SubStates        {get;}
    
        void        AddSubstate        (IState subState);
        void        RemoveSubstate    (IState subState);
        void        RemoveSubstate    (string subStateName);
        void        RemoveSubstate    (int substateIndex);
        IStateEventListenerContainer StateEventListener {get;}
        void        Entry            ();
        void        Do            ();
        void        Exit            ();
        void        Entry            (object info);
        void        Do            (object info);
        void        Exit            (object info);
    
    }


    или наследования от StateBase

    public class StateBase : IState
    {
    //...
        public virtual void Entry()
        {
            if (this.HasStateEventListener) FireEntryEvent();
        }
    
        public virtual void Entry(object info)
        {
            if (this.HasStateEventListener) FireEntryEvent(info);
        }
    
    
        public virtual void Do()
        {
            if (this.HasStateEventListener) FireDoEvent();
        }
    
        public virtual void Do(object info)
        {
            if (this.HasStateEventListener) FireDoEvent(info);
        }
    
    
        public virtual void Exit()
        {
            if (this.HasStateEventListener) FireExitEvent();
        }
    
        public virtual void Exit(object info)
        {
            if (this.HasStateEventListener) FireExitEvent(info);
        }
    //...
    }



    И обрабатывать например так :
    
    public abstract class Form1BaseListener : IStateEventListener
    {
        public Form1 Form = null;
    
    
        public Form1BaseListener(Form1 form)
        {
                Form = form;
        }
    
        public abstract bool OnStateEvent(IStateEvent Event);
    }
    
    public void SetUpTAB2()
    {
    
    GLOBAL_STATE.SubStates["TAB2"].SubStates[InitState.INIT_STATE_NAME].StateEventListener.Add(new InitTAB2Listener(this));
    GLOBAL_STATE.SubStates["TAB2"].SubStates["VIEWING"].StateEventListener.Add(new ViewingTAB2Listener(this));
    GLOBAL_STATE.SubStates["TAB2"].SubStates["CHANGING"].StateEventListener.Add(new ChangingTAB2Listener(this));
    GLOBAL_STATE.SubStates["TAB2"].SubStates["CANCELED"].StateEventListener.Add(new CanceledTAB2Listener(this));
    GLOBAL_STATE.SubStates["TAB2"].SubStates["VALIDATING"].StateEventListener.Add(new ValidatingTAB2Listener(this));
    GLOBAL_STATE.SubStates["TAB2"].SubStates["DELETING"].StateEventListener.Add(new DeletingTAB2Listener(this));
    GLOBAL_STATE.SubStates["TAB2"].SubStates[FinalState.FINAL_STATE_NAME].StateEventListener.Add(new FinalListener(this));
    
    new StateControllerBase(GLOBAL_STATE.SubStates["TAB2"]);
    }
    
    public class DeletingTAB2Listener : Form1BaseListener
    {
    public DeletingTAB2Listener(Form1 form):base(form){}
    
    public override bool OnStateEvent(IStateEvent Event)
    {
        if (Event is DoStateEvent)
        {
            this.Form.SetStatus("DeletingTAB2Listener "+Event.Source.Owner.StateName,Event.Source.StateName);
            if(MessageBox.Show("Löschen?","Frage",MessageBoxButtons.OKCancel,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2)==DialogResult.OK)
            {
                Event.Source.Owner.Controller.OnStateEvent(new StateEventBase("del_ok"));
            }
            else
            {
                Event.Source.Owner.Controller.OnStateEvent(new StateEventBase("del_error"));
            }                
                        
        }
    
        return true;
    }
    }


    Именно в этом и заключается корявость моей реализации, обработку actions и guard conditions надо будет вынести в отдельный блоки.

    P.S:
    Мой рантайм для State Machine находиться пока в преальфа состоянии ввиду отсутствия времени так что сильно не пинайте
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.