6.2.8. Интерфейсный класс
Класс, объявляющий интерфейс для взаимодействия с приложением, представлен в Листинг 97.
Листинг 97. Интерфейсный класс (ControlInterface.h)namespace sensor
{
class ISensorControl
{
public:
virtual ~ ISensorControl () = default;
virtual void initialize() = 0; // (1)
virtual void shutDown() = 0; // (2)
virtual void assignDriver(DriverPointer driver) = 0; // (3)
virtual DriverPointer getAssignedDriver() = 0; // (4)
virtual DriverPointer getSensorDriver(SensorNumber number) = 0; // (5)
virtual void addSensor(SensorType type, SensorNumber number) = 0; // (6)
virtual void deleteSensor(SensorNumber number) = 0; // (7)
virtual bool isSensorExist(SensorNumber number) = 0; // (8)
virtual bool isSensorOperable(SensorNumber number) = 0; // (9)
virtual SensorValue getSensorValue(SensorNumber number) = 0; // (10)
virtual void querySensorValue(SensorNumber number, SensorValueCallback callback) = 0; // (11)
virtual void readSensorValues(SensorValueCallback callback) = 0; // (12)
virtual SensorValue getMinValue(SensorNumber first, SensorNumber last) = 0; // (13)
virtual SensorValue getMaxValue(SensorNumber first, SensorNumber last) = 0; // (14)
virtual void setAlert(SensorNumber number, SensorAlertCallback callback, SensorValue alertValue, AlertRule alertRule, CheckAlertTimeout checkTimeoutSeс = 1) = 0; // (15)
virtual void resetAlert(SensorNumber number) = 0; // (16)
static ISensorControl* createControl(); // (17)
};
};
В строке 1 и 2 объявлены методы для запуска и останова. В строках 3 и 4 объявлены методы для назначения и получения драйвера. Этот драйвер должен быть создан и назначен в самом начале работы, поскольку он будет передаваться новым создаваемым датчикам. Узнать назначенный драйвер для соответствующего датчика можно в методе 5.
В строках 6 и 7 объявлены методы для добавления и удаления датчика. В методе 8 можно проверить, существует ли датчик с переданным номером, в методе 9 можно проверить, является ли датчик работоспособным.
В строке 10 объявлен метод для чтения текущего показания датчика. В методе 11 осуществляется асинхронный запрос показания датчика, значение будет возвращаться через передаваемый обратный вызов. В строке 12 осуществляется опрос показаний всех работоспособных датчиков, значения также возвращаются через обратный вызов. С помощью методов, объявленных в строках 13 и 14, можно получить минимальное и максимальное значение для набора датчиков с номерами из указанного диапазона.
В строке 15 назначается отслеживание пороговых значений, в строке 16 отслеживание выключается. С помощью метода, объявленного в строке 17, можно создать экземпляр соответствующего интерфейсного класса.
Класс, реализующий интерфейс, приведен в Листинг 98.
Листинг 98 Класс, реализующий интерфейс (SensorControl.h)namespace sensor
{
class ISensor;
class IDriver;
class CommandQueue;
class AlertControl;
class SensorContainer;
class SensorControl: public ISensorControl
{
public:
SensorControl();
~SensorControl();
void initialize() override;
/* Other Interface methods they are not displayed here*/
private:
SensorContainer* sensorContainer_; // (1)
CommandQueue* commandQueue_; // (2)
AlertControl* alertControl_; // (3)
bool isInitialized_; // (4)
DriverPointer driver_; // (5)
void checkInitialize(); // (6)
void checkDriver(); // (7)
};
}; //namespace sensor
В строке 1 объявлен контейнер для хранения датчиков, в строке 2 класс для выполнения асинхронных запросов, в строке 3 класс для отслеживания пороговых значений. Соответствующие указатели создаются в конструкторе и уничтожаются в деструкторе. Индикатор 4 указывает, была ли выполнена инициализация.
В строке 6 объявлен вспомогательный метод, который проверяет, была ли выполнена инициализация (если нет, выбрасывает исключение). В строке 7 аналогичный метод проверяет, был ли установлен драйвер.
Рассмотрим, как здесь используются обратные вызовы. Для начала самый простой случай чтение показаний работоспособных датчиков (Листинг 99).
Листинг 99. Обратные вызовы в классе, реализующем интерфейс (SensorControl.cpp)void SensorControl::readSensorValues(SensorValueCallback callback)
{
checkInitialize(); // (1)
Рассмотрим, как здесь используются обратные вызовы. Для начала самый простой случай чтение показаний работоспособных датчиков (Листинг 99).
Листинг 99. Обратные вызовы в классе, реализующем интерфейс (SensorControl.cpp)void SensorControl::readSensorValues(SensorValueCallback callback)
{
checkInitialize(); // (1)
sensorContainer_->forEachSensor([callback](SensorNumber number, SensorPointer sensor) // (2)
{
if (sensor->isOperable()) // (3)
{
callback(number, sensor->getValue()); // (4)
}
}
);
}
В строке 1 производится проверка, инициализирован ли класс. Если класс не проинициализирован, то функция выбросит исключение.
В строке 2 происходит перебор элементов контейнера, в качестве обратного вызова используется лямбда-выражение. Контейнер будет вызывать лямбда-выражение, в которое он будет передавать номер датчика и указатель на экземпляр класса. В теле выражения проверяется, является ли датчик работоспособным (строка 3), и если да, то выполняется соответствующий обратный вызов (строка 4).
Рассмотрим теперь поиск максимального и минимального значения для заданного диапазона номеров датчиков. Вначале разработаем вспомогательный класс, который будет последовательно принимать на вход показания датчиков и искать среди них максимальное и минимальное значение (Листинг 100).
Листинг 100. Класс для анализа минимального и максимального значения (SensorControl.cpp)class FindMinMaxValue
{
public:
enum MinMaxSign { MIN_VALUE = 0, MAX_VALUE = 1 }; // (1)
FindMinMaxValue(SensorNumber first, SensorNumber last, MinMaxSign sign) : // (2)
sign_(sign), first_(first), last_(last), count_(0)
{
if (sign == MIN_VALUE)
{
result_ = std::numeric_limits<SensorValue>::max(); // (3)
}
else
{
result_ = std::numeric_limits<SensorValue>::min(); // (4)
}
arrayFunMinMax_[MIN_VALUE] = &FindMinMaxValue::CompareMin; // (5)
arrayFunMinMax_[MAX_VALUE] = &FindMinMaxValue::CompareMax; // (6)
}
void operator()(SensorNumber number, SensorPointer sensor) // (7)
{
if ( sensor->isOperable() && (number >= first_ && number <= last_) ) // (8)
{
(this->*arrayFunMinMax_[sign_])(sensor->getValue()); // (9)
count_++; // (10)
}
}
SensorValue result() { return result_; } // (11)
size_t count() { return count_; } // (12)
private:
SensorNumber first; // (13)
SensorNumber last; // (14)
MinMaxSign sign; // (15)
SensorValue result; // (16)
size_t count; // (17)
using FunMinMax = void (FindMinMaxValue::*)(SensorValue value); // (18)
void CompareMin(SensorValue value) // (19)
{
if (result_ > value)
{
result_ = value;
}
}
void CompareMax(SensorValue value) // (20)
{
if (result_ < value)
{
result_ = value;
}
}
FunMinMax arrayFunMinMax_[2]; // (21)
};
В строке 2 объявлен конструктор, который принимает на вход следующие параметры: минимальное значение диапазона номеров; максимальное значение диапазона номеров; параметр, указывающий, что необходим поиск минимального либо максимального значения. В конструкторе инициализируются переменные класса: минимальное значение диапазона (объявлено в строке 13); максимальное значение диапазона (объявлено в 14); параметр для поиска (объявлено в 15); итоговый результат (объявлено в 16); количество датчиков, которые участвовали в поиске (объявлено в 17). В зависимости от переданного параметра начальный результат инициализируется соответственно максимальным либо минимальным значением (строки 3 и 4). Кроме того, инициализируется массив указателей на функцию (строки 5 и 6, объявление в 21). Данные функции предназначены для сравнения и запоминания максимального либо минимального значений (объявлены в 19 и 20).
Анализ очередного значения происходит в перегруженном операторе 7. На вход подаются номер датчика и указатель на датчик. Если датчик работоспособный и его номер попадает в заданный диапазон номеров (строка 8), то в зависимости от параметра поиска через указатель вызывается соответствующая функция для анализа (строка 9), а также увеличивается счетчик просмотренных датчиков (строка 10). Функции 11 и 12 возвращают итоговые результаты.