#include #include #include #include #include #include namespace modbus { namespace internal { response::response() noexcept {} response::response(constants::function_code function, const header_t& req_header, table* data_table) noexcept : adu{function}, req_header_{req_header}, data_table_{data_table} {} response::~response() {} bool response::initial_check(const packet_t& packet) { return packet.size() > header_length; } stage response::check_stage(const packet_t& packet) { // 1. check packet size (at least we got header and the function code) if (!initial_check(packet)) { // bad packet return stage::bad; } std::uint16_t tr, pr, len; std::uint8_t un, fun; struc::unpack(fmt::format(">{}", header_func_format), packet.data(), tr, pr, len, un, fun); #ifdef DEBUG_ON logger::debug( "Checking header: transaction(req[{:#04x}]=packet[{:#04x}]) " "protocol(req[{:#04x}]=packet[{:#04x}]) " "unit(req[{:#04x}]=packet[{:#04x}]) " "length(expected[{:#04x}]=packet[{:#04x}]", req_header_.transaction, tr, protocol, pr, req_header_.unit, un, len, (packet.size() - (header_length - 1))); #endif // 2. check transaction, protocol, and unit id if ((req_header_.transaction != tr) || (protocol != pr) || (req_header_.unit != un) || (len != (packet.size() - (header_length - 1)))) { // bad packet return stage::bad; } // 3. unpack packet and initialize header metadata decode_header(packet); // 4. check expected function is valid or not if (!check_function(function())) { // bad packet or defined class return stage::bad; } auto expected_function = utilities::to_underlying(function()); // 5. check expected function code equals with function code from packet if ((expected_function != function_code_) && !check_function(function_code_)) { std::uint8_t diff = function_code_ - 0x80; // 6. if expected function equals packet's function_code - 0x80 // exception is occured if (expected_function == diff) { return stage::error; } // bad packet return stage::bad; } // 7. all tests passed, return the "right" response return stage::passed; } void response::decode(const packet_t& packet) { try { switch (check_stage(packet)) { case internal::stage::bad: throw ex::bad_data(); case internal::stage::error: { // decode the packet auto exc = packet.at(header_length + 1); throw generate_exception(static_cast(exc), function(), header()); } break; default: decode_passed(packet); break; } } catch (const std::out_of_range&) { // anything happens, such as packet is malformed throw ex::bad_data(); } } } // namespace internal namespace response { error::error() noexcept {} packet_t error::encode() { calc_length(1); packet_t packet = header_packet(); packet.pop_back(); packet.reserve(header_length + 1 + 1); packet_t pdu = struc::pack(fmt::format(">{}", format), utilities::to_underlying(function()) + 0x80, utilities::to_underlying(ec_)); packet.insert(packet.end(), pdu.begin(), pdu.end()); if (packet.size() != calc_adu_length(1)) { throw ex::bad_data(); } return packet; } void error::decode(const packet_t& packet) { if (packet.size() != calc_adu_length(1)) { throw ex::bad_data(); } decode_header(packet); std::uint8_t ec; struc::unpack(fmt::format(">{}", format), packet.data() + header_length + 1, ec); if (!check_exception(ec)) { throw ex::bad_exception(); } ec_ = static_cast(ec); } } // namespace response } // namespace modbus