Program Listing for File channel.hpp
↰ Return to documentation for file (libspookyaction/include/pn532/channel.hpp
)
//
// Created by spak on 3/3/21.
//
#ifndef PN532_CHANNEL_REPL_HPP
#define PN532_CHANNEL_REPL_HPP
#include <chrono>
#include <mlab/bin_data.hpp>
#include <mlab/result.hpp>
#include <mlab/time.hpp>
#include <pn532/bits.hpp>
#include <pn532/log.h>
#include <pn532/msg.hpp>
namespace pn532 {
using mlab::bin_data;
using mlab::bin_stream;
using ms = std::chrono::milliseconds;
enum struct frame_type {
ack,
nack,
info,
error
};
template <frame_type>
struct frame {};
template <>
struct frame<frame_type::info> {
bits::transport transport = bits::transport::host_to_pn532;
command_code command = command_code::diagnose;
bin_data data;
};
class any_frame : public mlab::any_of<frame_type, frame, frame_type::error> {
public:
using mlab::any_of<frame_type, frame, frame_type::error>::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<frame_type::ack> const &);
bin_data &operator<<(bin_data &bd, frame<frame_type::nack> const &);
bin_data &operator<<(bin_data &bd, frame<frame_type::error> const &);
bin_data &operator<<(bin_data &bd, frame<frame_type::info> 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<bin_stream &, frame_id const &> 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 <class... Tn>
using result = mlab::result<channel_error, Tn...>;
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<bin_data::const_iterator> buffer, ms timeout) = 0;
virtual result<> raw_receive(mlab::range<bin_data::iterator> 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<any_frame> 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<bin_data> response(command_code cmd, ms timeout);
[[nodiscard]] result<bin_data> command_response(command_code cmd, bin_data data, ms timeout);
template <mlab::is_extractable Data>
[[nodiscard]] result<Data> command_parse_response(command_code cmd, bin_data data, ms timeout);
private:
[[nodiscard]] result<any_frame> receive_stream(ms timeout);
[[nodiscard]] result<any_frame> 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 <class... Tn, class... Args>
[[nodiscard]] inline result<Tn...> update(Args &&...args);
};
}// namespace pn532
namespace pn532 {
template <mlab::is_extractable Data>
result<Data> 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 <class... Tn, class... Args>
result<Tn...> comm_operation::update(Args &&...args) {
result<Tn...> retval{std::forward<Args>(args)...};
if (retval) {
_result = mlab::result_success;
} else {
_result = retval.error();
}
return retval;
}
}// namespace pn532
#endif//PN532_CHANNEL_REPL_HPP