235 lines
8.6 KiB
C++
235 lines
8.6 KiB
C++
|
|
/*
|
|||
|
|
* SerialPortAdapterWithCheck.hpp
|
|||
|
|
*
|
|||
|
|
* Created on: 23 августа. 2023 г.
|
|||
|
|
* Author: titov
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#ifndef SOURCE_DRIVER_SERIALPORTADAPTERWITHCHECK_HPP_
|
|||
|
|
#define SOURCE_DRIVER_SERIALPORTADAPTERWITHCHECK_HPP_
|
|||
|
|
|
|||
|
|
#include "../systemic/IHoldingRegister.hpp"
|
|||
|
|
|
|||
|
|
#include "SpiPortRoutineOperation.hpp"
|
|||
|
|
|
|||
|
|
#include "../peripheral/ISerialPort.hh"
|
|||
|
|
#include "../systemic/Timer.hh"
|
|||
|
|
|
|||
|
|
#include <cstddef>
|
|||
|
|
|
|||
|
|
namespace driver {
|
|||
|
|
/**
|
|||
|
|
* @brief Адаптер последовательного порта с дополнительной проверкой корректности отправленных данных
|
|||
|
|
* и выставлением соответствующего статуса.
|
|||
|
|
*
|
|||
|
|
* @tparam Frame Тип контейнера для данных
|
|||
|
|
* @tparam Latency Задержка
|
|||
|
|
* @tparam AutoSet отправлять ли данные, если они не были изменены
|
|||
|
|
* False - будут отправляться только измененные данные.
|
|||
|
|
* True - данные будут отправляться по таймеру. независимо от того, изменились ли они
|
|||
|
|
*/
|
|||
|
|
template<typename Frame, typename Latency = unsigned int, bool AutoSet = false>
|
|||
|
|
class SerialPortAdapterWithCheck : public systemic::IHoldingRegister<Frame> {
|
|||
|
|
public:
|
|||
|
|
// Задержка
|
|||
|
|
static Latency temp;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @param port Последовательный интерфейс для обмена данными
|
|||
|
|
* @param frame_size Размер кадра
|
|||
|
|
* @param period_time Период отправки/чтения данных
|
|||
|
|
* @param request_frame Фрейм для передачи заголовка
|
|||
|
|
* @param default_set_frame Фрейм для передачи данных
|
|||
|
|
* @param default_get_frame Фрейм для получения данных
|
|||
|
|
* @param check_sending_value_mask Маска для проверки корректности отправленных данных
|
|||
|
|
* @param locked Заблокироать выполнение обработки данных
|
|||
|
|
* @param lset Задержка на отправку данных
|
|||
|
|
* @param lget Задержка на чтение данных
|
|||
|
|
*/
|
|||
|
|
SerialPortAdapterWithCheck( peripheral::ISerialPort & port, std::size_t frame_size, systemic::time_t period_time,
|
|||
|
|
Frame request_frame, Frame default_set_frame, Frame default_get_frame, Frame check_sending_value_mask,
|
|||
|
|
bool locked = false,
|
|||
|
|
Latency & lset = temp, Latency & lget = temp );
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Отправить фрейм в последовательный порт.
|
|||
|
|
* @param frame фрейм данных
|
|||
|
|
*/
|
|||
|
|
void set( Frame frame ) final;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Прочитать последний полученный ответ из последовательного порта.
|
|||
|
|
* @result Фрейм данных
|
|||
|
|
*/
|
|||
|
|
Frame get() const final;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Заблокировать операции отправки/приема данных.
|
|||
|
|
*/
|
|||
|
|
void lock() final;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Разблокировать операции отправки/приема данных.
|
|||
|
|
*/
|
|||
|
|
void unlock() final;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Перезагружает адаптер.
|
|||
|
|
*/
|
|||
|
|
void reset();
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Запуск процесса чтения/отправки данных по последовательному порту.
|
|||
|
|
*/
|
|||
|
|
void process();
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief Завершилась ли последняя отправка ошибкой.
|
|||
|
|
* @return Результат последней отправки:
|
|||
|
|
* true - данные были испорчены при отправке
|
|||
|
|
* false - отправка завершилась успешно
|
|||
|
|
*/
|
|||
|
|
bool is_last_sending_error() const;
|
|||
|
|
|
|||
|
|
~SerialPortAdapterWithCheck() = default;
|
|||
|
|
private:
|
|||
|
|
enum { SET, GET, SWITCH, LOCKED } op, prev_op, stash_op;
|
|||
|
|
|
|||
|
|
// Статус последней отправки по последовательному порту
|
|||
|
|
enum { SUCCESS, FAILURE } last_sending_status_ = SUCCESS;
|
|||
|
|
|
|||
|
|
// Набор операций для драйвера SPI.
|
|||
|
|
typedef SpiOperationPack<Frame, Latency> Operation;
|
|||
|
|
|
|||
|
|
// Набор операций для отправки данных по SPI
|
|||
|
|
Operation set_op;
|
|||
|
|
// Набор операций для чтения данных по SPI
|
|||
|
|
Operation get_op;
|
|||
|
|
|
|||
|
|
// Фрейм для передачи данных
|
|||
|
|
Frame new_set_value;
|
|||
|
|
// Фрейм для получения данных
|
|||
|
|
Frame new_get_value;
|
|||
|
|
|
|||
|
|
// Период между операциями
|
|||
|
|
systemic::time_t period_time;
|
|||
|
|
// Отвечает за обработку операций, связанных с периодом
|
|||
|
|
systemic::Timer periodizator;
|
|||
|
|
|
|||
|
|
// Маска для проверки корректности последней отправки данных
|
|||
|
|
Frame check_sending_value_mask_;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
Latency driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::temp = 0;
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::SerialPortAdapterWithCheck(
|
|||
|
|
peripheral::ISerialPort & port, std::size_t frame_size, systemic::time_t period_time,
|
|||
|
|
Frame request_frame, Frame default_set_frame, Frame default_get_frame, Frame check_sending_value_mask,
|
|||
|
|
bool locked,
|
|||
|
|
Latency & _lset, Latency & _lget ) :
|
|||
|
|
set_op( port, frame_size, default_set_frame, _lset ), get_op( port, frame_size, request_frame, _lget ), new_set_value(default_set_frame), new_get_value(default_get_frame), period_time(period_time),
|
|||
|
|
op( locked ? LOCKED : GET ), prev_op(SWITCH), stash_op(GET), check_sending_value_mask_(check_sending_value_mask) {
|
|||
|
|
|
|||
|
|
periodizator.start( period_time );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline void driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::set( Frame set_value ) {
|
|||
|
|
new_set_value = set_value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline Frame driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::get() const {
|
|||
|
|
return new_get_value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline void driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::lock() {
|
|||
|
|
stash_op = op;
|
|||
|
|
op = LOCKED;
|
|||
|
|
periodizator.stop();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline void driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::unlock() {
|
|||
|
|
periodizator.start(period_time);
|
|||
|
|
op = stash_op;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline void driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::reset() {
|
|||
|
|
op = LOCKED;
|
|||
|
|
|
|||
|
|
periodizator.stop();
|
|||
|
|
|
|||
|
|
prev_op = SWITCH;
|
|||
|
|
stash_op = GET;
|
|||
|
|
|
|||
|
|
periodizator.start( period_time );
|
|||
|
|
|
|||
|
|
op = GET;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline void driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::process() {
|
|||
|
|
switch(op) {
|
|||
|
|
case SET: {
|
|||
|
|
|
|||
|
|
if( set_op() ) {
|
|||
|
|
prev_op = op;
|
|||
|
|
op = SWITCH;
|
|||
|
|
}
|
|||
|
|
} break;
|
|||
|
|
case GET: {
|
|||
|
|
|
|||
|
|
if( get_op() ) {
|
|||
|
|
|
|||
|
|
prev_op = op;
|
|||
|
|
op = SWITCH;
|
|||
|
|
new_get_value = get_op.output;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} break;
|
|||
|
|
case SWITCH: {
|
|||
|
|
if( periodizator.delayElapsed() ) {
|
|||
|
|
periodizator.start(period_time);
|
|||
|
|
|
|||
|
|
// Проверка на ошибку записи:
|
|||
|
|
if( prev_op == GET ) {
|
|||
|
|
|
|||
|
|
last_sending_status_ =
|
|||
|
|
// Проверка по маске - накладываем маску на поля и сравниваем их
|
|||
|
|
(set_op.input & check_sending_value_mask_) == (new_get_value & check_sending_value_mask_)
|
|||
|
|
// Стартовая проверка - чтобы исключить ложное срабатывание в начале работы
|
|||
|
|
|| (get_op.output == 0x00) ? SUCCESS : FAILURE;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if( prev_op == GET &&
|
|||
|
|
( FAILURE == last_sending_status_ || set_op.input != new_set_value || AutoSet ) ) {
|
|||
|
|
|
|||
|
|
set_op.input = new_set_value;
|
|||
|
|
op = SET;
|
|||
|
|
|
|||
|
|
} else
|
|||
|
|
op = GET;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
} break;
|
|||
|
|
case LOCKED: {
|
|||
|
|
} break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
template<typename Frame, typename Latency, bool AutoSet>
|
|||
|
|
inline bool driver::SerialPortAdapterWithCheck<Frame, Latency, AutoSet>::is_last_sending_error() const {
|
|||
|
|
return last_sending_status_ != SUCCESS;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endif /* SOURCE_DRIVER_SERIALPORTADAPTERWITHCHECK_HPP_ */
|