235 lines
7.3 KiB
C++
235 lines
7.3 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_ */
|