}
}
Пока не установлен индикатор завершения (устанавливается в методе stop), выполняется цикл 1. Вначале блокируется мьютекс 2 (это необходимо для корректной работы условной переменной), затем осуществляется ожидание условной переменной 3. Когда метод addCommand сформировал новую запись и добавил ее в контейнер, он инициирует срабатывание условной переменной, и поток выполнения переходит к циклу 4 (мьютекс при этом оказывается заблокирован). Этот цикл работает, пока очередь не опустеет либо будет установлен индикатор выхода.
В строке 5 из контейнера извлекается очередная запись, в строке 6 эта запись удаляется из контейнера. В строке 7 снимается блокировка мьютекса, что позволяет добавлять в контейнер новые записи, пока идет обработка очередной команды. В строке 8 осуществляется обратный вызов, в строке 9 мьютекс блокируется вновь, и далее повторяется цикл 4.
6.2.7. Наблюдатель
Объявление класса наблюдателя приведено в Листинг 95.
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.
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).