Реализация государственной машины
-
05-07-2019 - |
Вопрос
Я ищу какого-нибудь генерала
- Оптимизация
- Корректность
- Расширяемость
совет по моей текущей реализации иерархического конечного автомата на 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), которые переопределяют базовые значения.В корневом состоянии конечный автомат имеет набор переменных (функций, свойств ...), инициализированных некоторыми значениями по умолчанию.Каждый раз, когда мы входим в дочернее состояние, список "переопределений" определяет переменную и значения, которые должны заменить переменные и значения с тем же именем в родительском состоянии.Обновленный оригинал для большей наглядности.
Ссылающиеся переменные
Во время выполнения текущие состояния сохраняются в стеке.
Каждый раз, когда ссылаются на переменную, выполняется обход стека вниз в поисках наивысшего значения переопределения или, в случае отсутствия переопределений, значения по умолчанию.
Переключение состояний
Каждый раз, когда переключается на один кадр состояния, это состояние помещается в стек.
Каждый раз, когда переключается состояние, я отслеживаю нисхождение по дереву, которое переводит меня из текущего состояния в корневое.Затем я выполняю древовидный переход от целевого состояния к корневому, пока не увижу, что текущая трассировка совпадает с предыдущей трассировкой.Я объявляю пересечение там, где пересекаются эти 2 трассы.Затем, чтобы переключиться в целевое состояние, я спускаюсь от источника, извлекая фреймы состояния из стека, пока не достигну точки пересечения.Затем я поднимаюсь к целевому узлу и помещаю фреймы состояния в стек.
Итак, для приведенного выше примера кода
Трассировка выполнения для переключения состояний
- Исходное состояние = запись
Целевое состояние = alsoStreamToRemoteIp
происхождение от источника = запись-> корень (трассировка = [корень])
переход от target = alsoStreamToRemoteIp-> воспроизведение-> root (трассировка = [воспроизведение, root])
Пересекается в корне.
Чтобы переключиться с записи на alsoStreamToRemoteIp,
- Извлеките "запись" из стека (и вызовите ее функцию exit...здесь не определено).
- Нажмите "воспроизведение" на стек (и вызовите функцию ввода).
- Поместите "alsoStreamToRemoteIp" в стек (и вызовите функцию enter).
Решение
Две вещи:
1:В большинстве случаев просто представляйте состояние вашей программы в виде модели и взаимодействуйте с ней напрямую или через шаблон MVC.
2:Если вам действительно нужен FSM, т.е.вы хотите случайным образом выполнить с вашей моделью несколько действий, только некоторые из которых разрешены в определенное время.Затем....
По-прежнему сохраняйте состояние вашей программы в модели (или нескольких моделях в зависимости от декомпозиции и сложности) и представляйте состояния и переходы, например.
class State:
def __init__(self):
self.neighbors = {}
Где соседи содержат словарь {Action: State}
, чтобы вы могли сделать что-то вроде
someAction.execute() # Actions manipulate the model (use classes or lambdas)
currentState = currentState.neighbors[someAction]
Или, что еще круче, создать бесконечный цикл, случайным образом выбирающий действие из числа соседей, выполняющий его и перемещающий состояние на неопределенный срок.Это отличный способ протестировать вашу программу.
Другие советы
Я не уверен, что следую всем приведенным здесь деталям.Однако, похоже, что вы описываете реализацию FSM (конечного автомата), где у вас есть несколько конечных автоматов.Иногда, когда конкретное событие (E1) происходит в определенном состоянии (S1) FSM F1, вам необходимо ввести новый FSM (назовите его F2) для упрощения обработки в целом).
Если это так, то когда E1 происходит в S1, вам нужно вызвать процедуру действия, которая берет на себя чтение события и реализует F2 FSM.При вызове он начинает обработку в начальном состоянии F2 и обрабатывает соответствующие вложенные события.Когда он достигает своего конечного состояния, интерпретатор для F2 завершает работу.Это может вернуть некоторую информацию в процедуру действия F1, которая была приостановлена во время выполнения F2, и это может повлиять на следующее состояние в F1.
Остальная часть вашего описания - такие вещи, как "переопределение блоков" - не будут иметь особого смысла для людей, не имеющих доступа к вашей реализации.