• Программирование
  • C++
  • Визуальное моделирование сложных реагирующих систем при помощи диаграмм состояния UML Harel

Визуальное моделирование сложных реагирующих систем при помощи диаграмм состояния UML Harel - Как легко описать параллельность состояний?

ОГЛАВЛЕНИЕ

Как легко описать параллельность состояний?

Решение – позволить ортогональным состояниям работать одновременно.

Сложные состояния имеют одну или больше областей для субсостояний. Область – это просто контейнер для субсостояний.

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

Ортогональное состояние: Если сложное состояние можно разбить при помощи отношений на две или более совпадающих области, оно называется ортогональным состоянием.

Следующий макрос определяет ортогональное состояние под названием OrthoState как потомок родительского состояния Player. Действия при входе/выходе из состояния - OrthoStateEntry и OrthoStateExit, соответственно. Ортогональное состояние состоит из трех областей:

  • PlayerReg1 – экземпляр конечного автомата Player, выполняющийся в том же потоке, что и его родитель, с приоритетом 0.
  • PlayerReg2 - экземпляр конечного автомата Player, выполняющийся в отдельном потоке с приоритетом 0.
  • PlayerReg3 - экземпляр конечного автомата Player, выполняющийся в отдельном потоке с приоритетом 0.
SME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)
SME_ON_INIT_STATE(SME_NULL_ACTION, PowerDown)
SME_END_STATE_DEF

SME_BEGIN_ORTHO_SUB_STATE_DEF_P(OrthoState)
SME_ON_EVENT(EXT_EVENT_ID_POWER, OnPowerDownEXT_EVENT_ID_POWER, Join1)
SME_END_STATE_DEF

SME_BEGIN_ORTHO_COMP_STATE_DEF(OrthoState, Player, OrthoStateEntry, OrthoStateExit)
SME_REGION_DEF(PlayerReg1,Player,SME_RUN_MODE_PARENT_THREAD,0)
SME_REGION_DEF(PlayerReg2,Player,SME_RUN_MODE_SEPARATE_THREAD,0)
SME_REGION_DEF(PlayerReg3,Player,SME_RUN_MODE_SEPARATE_THREAD,0)
SME_END_ORTHO_STATE_DEF

При помощи StateWizard можно определить переход из/в ортогональное состояние, используя SME_BEGIN_ORTHO_SUB_STATE_DEF(). Но нельзя определить явный вход в одно из дочерних состояний области. Все области работают в виде приложения, которое выполняется одновременно. При входе в ортогональное состояние все области активируются автоматически. И при выходе из состояния все области деактивируются автоматически. Если несколько областей выполняются в своих отдельных потоках, ортогональное состояние отправит события SME_EVENT_EXIT_LOOP этим областям и будет ждать завершения работы этих потоков.

Как моделировать встроенный таймер состояния для системы реального времени?

В системах реального времени необходимо моделировать таймеры. Ядро поддерживает два типа таймеров: встроенные таймеры состояний и обычные таймеры.

Встроенные таймеры состояний вплотную работают с конечным автоматом. Они управляются ядром. При входе в состояние автоматически запускается встроенный таймер, если он доступен. При выходе из состояния он останавливается. При простое они запускают события SME_EVENT_STATE_TIMER. Ядро будет предпринимать действия на основе определения конечного автомата, используя SME_ON_STATE_TIMEOUT.

Обычные таймеры – это явные таймеры. Разработчики должны запускать или останавливать их при помощи вызовов API. При простое они запускают события SME_EVENT_TIMER. Ядро предоставляет два типа режимов обработки, вызов функции обратного вызова или обработка события, которые определяются, как показано ниже:

enum {SME_TIMER_TYPE_CALLBACK, SME_TIMER_TYPE_EVENT};

Явная функция обратного вызова должна определяться для таймера режима функции обратного вызова. Этот режим не зависит от определений конечного автомата. Для режима события таймера SME_ON_EVENT(SME_EVENT_TIMER, handler(обработчик), new state(новое состояние) должно быть определено для обработки события простоя.

Следующий пример определяет  таймер на 3000 мс в состоянии Player и таймер на 9000 мс в состоянии Playing. При простое будет вызвано действие PowerUpTimeOut. При входе в состояние Playing запустите его. При простое перейдите в состояние Pause автоматически с выполнением действия PlayingTimeOut.

#define SME_CURR_DEFAULT_PARENT PowerUp

SME_BEGIN_COMP_STATE_DEF(PowerUp, Player, PowerUpEntry, PowerUpExit)
SME_ON_INIT_STATE(OnPowerUpInitChild, Playing)
SME_ON_STATE_TIMEOUT_INTERNAL_TRAN(3000, PowerUpTimeOut)
SME_END_STATE_DEF

SME_BEGIN_LEAF_STATE_DEF_P(Playing, PlayingEntry, PlayingExit)
SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,
             OnPlayingEXT_EVENT_ID_PAUSE_RESUME,Pause)
SME_ON_INTERNAL_TRAN_WITH_GUARD(SME_EVENT_TIMER,
                GuardTimer2_func,OnTimer2Proc) /* Событие обычного таймера */
SME_ON_STATE_TIMEOUT(9000, PlayingTimeOut, Pause)
/* Переход в состояние паузы, если простой длится 9 секунд. */

SME_END_STATE_DEF

SME_BEGIN_LEAF_STATE_DEF_P(Pause, PauseEntry, PauseExit)
SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,
             OnPauseEXT_EVENT_ID_PAUSE_RESUME,Playing)
SME_END_STATE_DEF

SME_END_COMP_STATE_DEF(PowerUp)

Как моделировать псевдосостояния?

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

Условное (разветвляющееся) псевдосостояние – это краткое обозначение для  многочисленных выходящих переходов, из которых  все запускаются одним и тем же событием, но каждый из которых имеет различные защиты.

Объединенное псевдосостояние – это состояние с несколькими  входящими переходами и с одним исходящим переходом.

При помощи API среды разработки приложений StateWizard условное псевдосостояние Cond1 и объединенное псевдосостояние Join1 определяются, как показано ниже. Псевдосостояние Cond1 имеет три ответвления: COND1, COND2 и COND_ELSE. Выходное целевое состояние зависит от возвращаемого значения функции Cond1_func. Выполняемое действие при входе в псевдосостояние Join1 - функция JoinAct.

#define SME_CURR_DEFAULT_PARENT Player

SME_BEGIN_ROOT_COMP_STATE_DEF(Player, PlayerEntry, PlayerExit)
SME_ON_INIT_STATE(SME_NULL_ACTION, PowerDown)
SME_END_STATE_DEF
....
SME_BEGIN_COND_STATE_DEF_P(Cond1, Cond1_func)
SME_ON_EVENT(COND_EV_COND1, CondAct1, Playing)
SME_ON_EVENT(COND_EV_COND2, CondAct2, Pause)
SME_ON_EVENT(SME_EVENT_COND_ELSE, CondActElse, PowerUp)
SME_END_STATE_DEF

SME_BEGIN_JOIN_STATE_DEF_P(Join1)
SME_ON_JOIN_TRAN(JoinAct, Cond1)
SME_END_STATE_DEF
...