/* * 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 namespace driver { /** * @brief Адаптер последовательного порта с дополнительной проверкой корректности отправленных данных * и выставлением соответствующего статуса. * * @tparam Frame Тип контейнера для данных * @tparam Latency Задержка * @tparam AutoSet отправлять ли данные, если они не были изменены * False - будут отправляться только измененные данные. * True - данные будут отправляться по таймеру. независимо от того, изменились ли они */ template class SerialPortAdapterWithCheck : public systemic::IHoldingRegister { 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 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 Latency driver::SerialPortAdapterWithCheck::temp = 0; template inline driver::SerialPortAdapterWithCheck::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 inline void driver::SerialPortAdapterWithCheck::set( Frame set_value ) { new_set_value = set_value; } template inline Frame driver::SerialPortAdapterWithCheck::get() const { return new_get_value; } template inline void driver::SerialPortAdapterWithCheck::lock() { stash_op = op; op = LOCKED; periodizator.stop(); } template inline void driver::SerialPortAdapterWithCheck::unlock() { periodizator.start(period_time); op = stash_op; } template inline void driver::SerialPortAdapterWithCheck::reset() { op = LOCKED; periodizator.stop(); prev_op = SWITCH; stash_op = GET; periodizator.start( period_time ); op = GET; } template inline void driver::SerialPortAdapterWithCheck::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 inline bool driver::SerialPortAdapterWithCheck::is_last_sending_error() const { return last_sending_status_ != SUCCESS; } #endif /* SOURCE_DRIVER_SERIALPORTADAPTERWITHCHECK_HPP_ */