.. _program_listing_file_libspookyaction_include_pn532_channel.hpp: Program Listing for File channel.hpp ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``libspookyaction/include/pn532/channel.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp // // Created by spak on 3/3/21. // #ifndef PN532_CHANNEL_REPL_HPP #define PN532_CHANNEL_REPL_HPP #include #include #include #include #include #include #include namespace pn532 { using mlab::bin_data; using mlab::bin_stream; using ms = std::chrono::milliseconds; enum struct frame_type { ack, nack, info, error }; template struct frame {}; template <> struct frame { bits::transport transport = bits::transport::host_to_pn532; command_code command = command_code::diagnose; bin_data data; }; class any_frame : public mlab::any_of { public: using mlab::any_of::any_of; }; struct frame_id { static constexpr std::size_t min_frame_length = bits::start_of_packet_code.size() + std::max(std::max(bits::ack_packet_code.size(), bits::nack_packet_code.size()), bits::fixed_extended_packet_length.size()); static constexpr std::size_t max_min_info_frame_header_length = min_frame_length + 1 /* preamble */ + 3 /* extended info frame length */; frame_type type = frame_type::error; bool has_preamble = false; std::size_t frame_total_length = min_frame_length; std::size_t info_frame_data_size = 0; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS bin_data &operator<<(bin_data &bd, frame const &); bin_data &operator<<(bin_data &bd, frame const &); bin_data &operator<<(bin_data &bd, frame const &); bin_data &operator<<(bin_data &bd, frame const &f); bin_data &operator<<(bin_data &bd, any_frame const &f); bin_stream &operator>>(bin_stream &s, any_frame &f); bin_stream &operator>>(std::tuple s_id, any_frame &f); bin_stream &operator>>(bin_stream &s, frame_id &id); #endif enum struct channel_error { timeout, hw_error, malformed, app_error }; template using result = mlab::result; enum struct comm_dir { send, receive }; enum struct comm_rx_mode { stream, buffered }; class channel { public: virtual ~channel() = default; protected: friend class comm_operation; virtual result<> raw_send(mlab::range buffer, ms timeout) = 0; virtual result<> raw_receive(mlab::range buffer, ms timeout) = 0; [[nodiscard]] virtual comm_rx_mode raw_receive_mode() const = 0; virtual bool on_receive_prepare(ms timeout) { return true; } virtual void on_receive_complete(result<> const &outcome) {} virtual bool on_send_prepare(ms timeout) { return true; } virtual void on_send_complete(result<> const &outcome) {} result<> send(any_frame const &frame, ms timeout); [[nodiscard]] result receive(ms timeout); public: virtual bool wake() = 0; result<> send_ack(bool ack_value, ms timeout); result<> receive_ack(bool ack_value, ms timeout); result<> command(command_code cmd, bin_data data, ms timeout); [[nodiscard]] result response(command_code cmd, ms timeout); [[nodiscard]] result command_response(command_code cmd, bin_data data, ms timeout); template [[nodiscard]] result command_parse_response(command_code cmd, bin_data data, ms timeout); private: [[nodiscard]] result receive_stream(ms timeout); [[nodiscard]] result receive_restart(ms timeout); bool _has_operation = false; }; #ifndef DOXYGEN_SHOULD_SKIP_THIS [[nodiscard]] const char *to_string(frame_type type); [[nodiscard]] const char *to_string(channel_error e); #endif class comm_operation { channel &_owner; comm_dir _event; result<> _result; public: comm_operation(channel &owner, comm_dir event, ms timeout); ~comm_operation(); [[nodiscard]] inline bool ok() const; [[nodiscard]] inline channel_error error() const; [[nodiscard]] inline channel_error update(channel_error e); [[nodiscard]] inline result<> update(bool operation_result); template [[nodiscard]] inline result update(Args &&...args); }; }// namespace pn532 namespace pn532 { template result channel::command_parse_response(command_code cmd, bin_data data, ms timeout) { if (const auto res_cmd = command_response(cmd, std::move(data), timeout); res_cmd) { bin_stream s{*res_cmd}; auto retval = Data(); s >> retval; if (s.bad()) { PN532_LOGE("%s: could not parse result from response data.", to_string(cmd)); return channel_error::malformed; } if (not s.eof()) { PN532_LOGW("%s: stray data in response (%d bytes).", to_string(cmd), s.remaining()); } return retval; } else { return res_cmd.error(); } } bool comm_operation::ok() const { return bool(_result); } channel_error comm_operation::error() const { return _result.error(); } channel_error comm_operation::update(channel_error e) { _result = e; return e; } result<> comm_operation::update(bool operation_result) { if (operation_result) { _result = mlab::result_success; } else { _result = channel_error::timeout; } return _result; } template result comm_operation::update(Args &&...args) { result retval{std::forward(args)...}; if (retval) { _result = mlab::result_success; } else { _result = retval.error(); } return retval; } }// namespace pn532 #endif//PN532_CHANNEL_REPL_HPP