Вопрос

Я ищу какого-нибудь генерала

  1. Оптимизация
  2. Корректность
  3. Расширяемость

совет по моей текущей реализации иерархического конечного автомата на 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,

  1. Извлеките "запись" из стека (и вызовите ее функцию exit...здесь не определено).
  2. Нажмите "воспроизведение" на стек (и вызовите функцию ввода).
  3. Поместите "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.

Остальная часть вашего описания - такие вещи, как "переопределение блоков" - не будут иметь особого смысла для людей, не имеющих доступа к вашей реализации.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top