Я ищу какого-то общего
- Оптимизация
- Правильность
- Расширяемость
совет по моей текущей реализации иерархического конечного автомата C++.
Образец
variable isMicOn = false
variable areSpeakersOn = false
variable stream = false
state recording
{
//override block for state recording
isMicOn = true //here, only isMicOn is true
//end override block for state recording
}
state playback
{
//override block for state playback
areSpeakersOn = true //here, only areSpeakersOn = true
//end override block for state playback
state alsoStreamToRemoteIp
{
//override block for state alsoStreamToRemoteIp
stream = true //here, both areSpeakersOn = true and stream = true
//end override block for state alsoStreamToRemoteIp
}
}
goToState(recording)
goToState(playback)
goToState(playback.alsoStreamToRemoteIp)
Реализация
В настоящее время HSM реализован в виде древовидной структуры, в которой каждое состояние может иметь различное количество дочерних состояний.
Каждое состояние содержит переменное количество блоков «переопределения» (в std::map), которые переопределяют базовые значения. В корневом состоянии конечный автомат имеет набор переменных (функций, свойств...), инициализированных некоторыми значениями по умолчанию. Каждый раз, когда мы входим в дочернее состояние, список «переопределений» определяет переменную и значения, которые должны заменить переменные и значения с тем же именем в родительском состоянии. Обновленный оригинал для ясности.
Ссылочные переменные
Во время выполнения текущие состояния сохраняются в стеке.
Каждый раз при ссылке на переменную выполняется обход стека вниз в поисках наивысшего переопределения или, в случае отсутствия переопределений, значения по умолчанию.
Переключение состояний
Каждый раз, когда переключается на кадр с одним состоянием, это состояние помещается в стек.
Каждый раз, когда происходит переключение в состояние, я отслеживаю спуск по дереву, который ведет меня от текущего состояния к корневому состоянию. Затем я выполняю спуск по дереву от целевого состояния к корневому состоянию, пока не увижу, что текущая трасса соответствует предыдущей трассе. Я объявляю пересечение там, где встречаются эти две трассы. Затем, чтобы переключиться в целевое состояние, я спускаюсь из источника, извлекая кадры состояния из стека, пока не достигну точки пересечения. Затем я поднимаюсь к целевому узлу и помещаю кадры состояния в стек.
Итак, для примера кода выше
Трассировка выполнения для переключения состояния
- Исходное состояние = запись
Целевое состояние = alsoStreamToRemoteIp
происхождение от источника = запись-> корень (трассировка = [корень])
происхождение от target = alsoStreamToRemoteIp->playback->root (trace = [playback, root])
Пересекается в корне.
Чтобы переключиться с записи на alsoStreamToRemoteIp,
- Извлеките «запись» из стека (и вызовите ее функцию выхода... здесь не определено).
- Поместите «воспроизведение» в стек (и вызовите функцию ввода).
- Поместите "alsoStreamToRemoteIp" в стек (и вызовите функцию ввода).