Выбрать главу

  }

}

Пока не установлен индикатор завершения (устанавливается в методе stop), выполняется цикл 1. Вначале блокируется мьютекс 2 (это необходимо для корректной работы условной переменной), затем осуществляется ожидание условной переменной 3. Когда метод addCommand сформировал новую запись и добавил ее в контейнер, он инициирует срабатывание условной переменной, и поток выполнения переходит к циклу 4 (мьютекс при этом оказывается заблокирован). Этот цикл работает, пока очередь не опустеет либо будет установлен индикатор выхода.

В строке 5 из контейнера извлекается очередная запись, в строке 6 эта запись удаляется из контейнера. В строке 7 снимается блокировка мьютекса, что позволяет добавлять в контейнер новые записи, пока идет обработка очередной команды. В строке 8 осуществляется обратный вызов, в строке 9 мьютекс блокируется вновь, и далее повторяется цикл 4.

6.2.7. Наблюдатель

Объявление класса наблюдателя приведено в Листинг 95.

Листинг 95. Наблюдатель – класс для отслеживания пороговых значений (Observer.h)

class Observer

{

public:

  void start();  // (1)

  void stop();   // (2)

  void addAlert(SensorNumber number, SensorPointer pointer, SensorAlertCallback callback, SensorValue alertValue, AlertRule alertRule, CheckAlertTimeout checkTimeoutSeс);  // (3)

  void deleteAlert(SensorNumber number);  // (4)

private:

  struct Alert  // (5)

  {

    Alert() {}

    Alert(SensorAlertCallback callback, SensorValue alertValue, AlertRule alertRule, SensorPointer sensor, CheckAlertTimeout checkTimeout):

    callback(callback), alertValue(alertValue), alertRule(alertRule), sensor(sensor), checkTimeout(checkTimeout), currentTimeout(0)

    {

    }

    SensorAlertCallback callback;

    SensorValue alertValue;

    AlertRule alertRule;

    SensorPointer sensor;

    CheckAlertTimeout checkTimeout;

    CheckAlertTimeout currentTimeout;

  };

  std::map<SensorNumber, Alert> containerAlert;  // (6)

  std::thread pollThread_;                       // (7)

  bool exit_;                                    // (8)

  std::mutex mutex_;                             // (9)

  void poll();                                   // (10)

};

В строке 1 объявлен метод для запуска процесса отслеживания пороговых значений, в строке 2 – метод для останова. Метод в строке 3 добавляет датчик для отслеживания, метод 4 – удаляет.

В строке 5 объявлена структура, в которой хранятся данные, необходимые для отслеживания показаний датчика. В строке 6 объявлен контейнер для хранения указанных структур; метод addAlert добавляет запись в контейнер, метод deleteAlert удаляет ее. В строке 7 объявлен класс для запуска потока для отслеживания, в строке 8 объявлен индикатор выхода, в строке 9 объявлен мьютекс для синхронизации.

Отслеживание показаний реализовано в методе, объявленном в строке 10. Поток отслеживания вызывает этот метод, который циклически опрашивает назначенные датчики и в случае превышения пороговых значений осуществляет обратный вызов. Реализация приведена в Листинг 96.

Листинг 96. Отслеживание пороговых значений

void Observer::poll()

{

  using namespace std::chrono_literals;

  while (!exit_)  // (1)

  {

    std::this_thread::sleep_for(1s);           // (2)

    std::lock_guard<std::mutex> lock(mutex_);  // (3)

    for (auto& item : containerAlert)  // (4)

    {

      Alert& alert = item.second;

      alert.currentTimeout++;          // (5)

      if (alert.checkTimeout != 0 && alert.currentTimeout >= alert.checkTimeout)  // (6)

      {

          bool triggerAlert = false;

          if (alert.alertRule == AlertRule::More)  // (7)

          {

            triggerAlert = alert.sensor->getValue() > alert.alertValue;

          }

          else               // (8)

          {

            triggerAlert = alert.sensor->getValue() < alert.alertValue;

          }

          if (triggerAlert)  // (9)

          {

              alert.checkTimeout = alert.callback(item.first, alert.alertValue);  // (10)

          }

          alert.currentTimeout = 0;  // (11)

        }

    }

  }

}

В строке 1 объявлен цикл опроса, который выполняется, пока не выставлен индикатор завершения (выставляется в методе stop). В строке 2 поток засыпает на 1 секунду, т. е. интервал опроса равен 1 секунде. В строке 3 блокируется мьютекс, чтобы избежать коллизий добавления/удаления элементов в контейнере.

В строке 4 осуществляется опрос элементов, хранящихся в контейнере. Текущее время опроса в строке 5 увеличивается на единицу. Если уведомление разрешено, о чем говорит ненулевое значение timeout, и время последнего опроса превысило назначенное время (строка 6), то тогда проверяется, имелось ли превышение пороговых значений в соответствии с назначенными правилами (строки 6, 7). Если превышение зафиксировано (строка 9), то осуществляется обратный вызов (строка 10). Этот вызов возвращает следующий интервал опроса, после чего текущее время сбрасывается (строка 11).