На этапе проектирования мы должны четко определить структуру проекта. Это дает активный фундамент на основе, которого можно строить отдельные функциональные части системы. Существует целый ряд архитектурных моделей для решения задач сбора и обработки данных, но наиболее часто встречается синхронизация автономных исполнителей и схема по кадровой обработки.
Первая архитектура часто применяется при проектировании распределенных систем.
Вторая более подходит для систем жесткого реального времени с большей предсказуемостью процесса обработки. Для метеостанции больше подходит второй вариант.
Кадр
При по кадровой обработке процесс мониторинга осуществляется как последовательность считывания обработки и вывода на экран значений параметров через определенные промежутки времени.
Приведем диаграмму классов отображающею особенности архитектуры системы.
На приведенной диаграмме присутствуют в основном те же классы которые были определены на этапе анализа. Отличие в том что на этой диаграмме видно каким образом ключевые абстракции нашего ключевого приложения взаимодействуют друг с другом. Например, не воспроизведена иерархия классов датчиков. Кроме этого вводится новый класс, Sensors который, служит для объединения в контейнер всех объектов датчика. Поскольку, по крайней мере, два объекта (Sample и InputManager) в нашей системе должны ассоциироваться с целой коллекцией датчика. Помещение их в один контейнерный класс позволяет рассматривать все датчики единым образом.
Механизм покадровой обработки:
Поведение системы в основном определяется взаимодействием классов Sampler и Timer. Определим вначале внешний интерфейс для класса Timer, осуществляющего диспетчеризацию функции обратного вызова.
typedef unsigned int Tick
class Timer{
public:
static setCallBack(void(*)(Tick));
static startTiming();
static Tick numberofTick();
private:
…
};
Прежде чем перейти к классу Sampler, введем перечисляемый тип всех датчиков, присутствующих в системе:
enum SensorName{Direction, Speed, WindChill, Temperature, DewPoint, Humidity, Pressure};
class Sampler{
public:
Sampler();
~Sampler();
//установить частоту опроса
void setSamplingRate(SensorName, Tick);
void Sample (Tick);//опрос
Tick SamplingRate() const;//частота опроса
protected:
…};
Чтобы обеспечить связь м/у классами Timer и Sampler, требуется некоторое дополнение. Определим фрагмент кода, в котором создается объект класса Sampler, и определяется внеклассовая функция acquire.
Sampler sampler;
Void acquire(Tick){
sampler.sample(t)
}
После этого можно написать функцию main, где происходит присоединение к Timer-у функции обратного вызова и запускается процесс опроса датчика:
main() {
Timer:: setCallBack(acquire);
Timer:: startTiming();
while(1){
;
}
return (0);
}
Это довольно типичная для ОО-системы главная функция. Она короткая (т.к. основная работа делегирована объектом) и включает в себя цикл диспетчеризации. (В нашем случае пустой, т.к. отсутствуют какие-либо фоновые процессы)
Далее определяем внешний интерфейс класса Sensors.
class Sensors: protected collections{
public:
Sensors();
virtual ~Sensors();
void add sensor (const sensor&sensorName, unsigned int id=0);
unsigned int number of Sensors() const;
unsigned int number of Sensors(sensorName);
sensor&sensor(sensorName, unsigned int id=0 );
protected:
… };
Это в основном класс-коллекция, поэтому он объявляется подклассом фундаментального класса Сollection. Класс Сollection указан как защищенный супер класс. Это сделано для того, чтобы скрыть детали его строения от клиентов класса Sensors.
Т.о. создан класс-коллекция для датчиков, который может содержать мн-во экземпляров датчиков одного и того же типа, при чем каждый экземпляр своего вида имеет уникальный идентификационный номер, начиная с нуля.
Вернемся к спецификации класса Sampler. Необходимо обеспечить его ассоциацию с классом Sensors и DisplayManager:
class Sampler{
public:
Sampler(Sensors&DisplayManager);
protected:
Sensores& rep Sensors;
DisplayManager& rep DisplayManager ;
};
Приведем фрагмент кода, где происходит создание класса Sampler:
Sensors sensors;
DisplayManager display;
Sampler sampler(sensors,display);
При порождении объекта Sampler устанавливается связь м/у ним, коллекцией датчиков Sensors и экземпляром класса DisplayManager, который будет использоваться системой.
В заключение следует заняться описанием центральной операции класса Sampler — sample.
void Sampler :: sample(Tick t){
for (SensorName name=Direction; name<=Pressure; name++)
for(unsigned int id=0; id<=rep sensors.numberOfSensors(name); id++)
if(!(t % SamplingRate(name)))
rep DisplayManager.display(rep Sensors.sensor(name,id).currentValue(),name,id);
}
Эта функция по очереди опрашивает каждый тип датчика, и каждый датчик внутри типа. Она проверяет, пришло ли время считывать информацию с датчика, и, если да, то определяет ссылку на датчик в коллекции, считывает его текущее значение и передает его менеджеру дисплея, ассоциированному с данным экземпляром класса Sampler.
Семантика этой операции основывается на полиморфном поведении сл. метода:
}/ virtual float current value();
определенного для базового класса Sensor. Эта операция кроме того основывается на функции класса DisplayManager.display.
Диаграмма классов механизма по кадровой обработки.