Машина состояний. Что это такое и зачем это нужно.

Тема в разделе "Флудилка", создана пользователем Megakoteyka, 20 дек 2013.

  1. Megakoteyka

    Megakoteyka Оракул Модератор

    Довольно часто реализовать некоторый сложный алгоритм можно, используя такую штуку, как машина состояний aka конечный автомат.
    Например, мы хотим запрограммировать реализацию некоторого простого протокола:
    Чтобы запрограммировать подобное общение клиента с сервером, можно заводить кучу флагов и использовать кучу условий, но гораздо проще поступить иначе:
    Код (Text):
    typedef enum ESTEPS {
      STEP1_HELLO,
      STEP2_LOGIN,
      STEP3_PASS,
      STEP4_WORK
    };

    // текущее состояние алгоритма
    ESTEPS _step = STEP1_HELLO;
    // строка с ответом сервера
    char _stringFromServer[64];
    // флаг "сервер ответил"
    boolean _stringFromServerReceived = false;

    void setup()
    {
      // ...
    }

    void loop()
    {
      /*
      тут будет код, который принимает данные от сервера,
      собирает их в строку _stringFromServer и устанавливает
      флаг _stringFromServerReceived по окончанию сборки строки
      */
      switch(_step)
      {
        // начинаем с приветствия
        case STEP1_HELLO:
          SendToServer("hello");
          // если сервер ответил и строка соответствует нашим ожиданиям...
          if(_stringFromServerReceived && strcmp(_stringFromServer, "hello, login please") == 0)
            _step = STEP2_LOGIN;// ... переходим к шагу 2
          break;
        // отправляем логин
        case STEP2_LOGIN:
          SendToServer("VASYA");
          // если сервер ответил и строка соответствует нашим ожиданиям...
          if(_stringFromServerReceived && strcmp(_stringFromServer, "pass please") == 0)
            _step = STEP3_PASS;// ... переходим к шагу 3
          break;
        // отправляем пароль
        case STEP3_PASS:
          SendToServer("******");
          // если сервер ответил и строка соответствует нашим ожиданиям...
          if(_stringFromServerReceived && strcmp(_stringFromServer, "welcome") == 0)
            _step = STEP4_WORK;// ... переходим к шагу 4
          else
            _step = STEP1_HELLO;// ... иначе возвращаемся в начало алгоритма
          break;
        // работаем
        case STEP4_WORK:
          SendToServer("ку!");
          // если сервер ответил...
          if(_stringFromServerReceived)
          {
            // ... то в _stringFromServer будет лежать искомое "ы-ы-ы!"
            // _step = STEPx_NEXT_STEP // переходим к очередному состоянию
          }    
          break;
      }
    }

    void SendToServer(char* message)
    {
      _stringFromServerReceived = false;
      // SendMessageToServer(message);
    }
    Нетрудно заметить, что при таком подходе можно реализовать любую комбинацию переходов между состояниями алгоритма в зависимости от результата вычислений, произведенных на текущем шаге. Логика каждого шага изолирована от других шагов и ее легко и приятно отлаживать. Кроме того, выполнение алгоритма не блокирует контроллер и можно одновременно общаться с сервером и мигать светодиодом.
     
    zsm нравится это.
  2. -Mark-

    -Mark- Гик

  3. BlitzKeir

    BlitzKeir Нуб

    client: "ку!"
    server: "ы-ы-ы!"

    Улыбнуло :)