110 lines
4.8 KiB
C++
110 lines
4.8 KiB
C++
// 本文件测试底层 Modbus RTU 协议实现:
|
|
// CRC16 是否匹配文档示例帧、大端寄存器打包是否正确、TS+XYZABC 位姿转换
|
|
// 是否可往返,以及请求/响应解析器是否严格校验。
|
|
|
|
#include "line_laser_modbus/protocol.hpp"
|
|
#include "test_support.hpp"
|
|
|
|
#include <array>
|
|
#include <iostream>
|
|
|
|
using namespace line_laser_modbus;
|
|
|
|
namespace {
|
|
|
|
void test_documented_read_frames() {
|
|
test_support::require_bytes_equal(
|
|
build_read_request(kDefaultSlaveId, kModeCommandAddress, 2U),
|
|
ByteVector{0x08, 0x03, 0xD0, 0x00, 0x00, 0x02, 0xFC, 0x52},
|
|
"read mode/state request must match protocol document");
|
|
|
|
test_support::require_bytes_equal(
|
|
build_read_request(kDefaultSlaveId, kCurrentPoseAddress, kPoseRegisterCount),
|
|
ByteVector{0x08, 0x03, 0xD0, 0x0A, 0x00, 0x0E, 0xDC, 0x55},
|
|
"read current pose request must match protocol document");
|
|
}
|
|
|
|
void test_documented_mode_write_frames() {
|
|
const std::array<ByteVector, 6U> expected_frames{{
|
|
{0x08, 0x10, 0xD0, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x1D, 0xCD},
|
|
{0x08, 0x10, 0xD0, 0x00, 0x00, 0x01, 0x02, 0x00, 0x01, 0xDC, 0x0D},
|
|
{0x08, 0x10, 0xD0, 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x9C, 0x0C},
|
|
{0x08, 0x10, 0xD0, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x5D, 0xCC},
|
|
{0x08, 0x10, 0xD0, 0x00, 0x00, 0x01, 0x02, 0x00, 0x04, 0x1C, 0x0E},
|
|
{0x08, 0x10, 0xD0, 0x00, 0x00, 0x01, 0x02, 0x00, 0x05, 0xDD, 0xCE},
|
|
}};
|
|
|
|
for (std::size_t mode = 0; mode < expected_frames.size(); ++mode) {
|
|
test_support::require_bytes_equal(
|
|
build_write_request(kDefaultSlaveId, kModeCommandAddress,
|
|
RegisterVector{static_cast<std::uint16_t>(mode)}),
|
|
expected_frames[mode],
|
|
"mode write request must match protocol document");
|
|
}
|
|
}
|
|
|
|
void test_pose_register_round_trip() {
|
|
const Pose6D source{1000U, 1.0F, 2.0F, 3.0F, 0.0F, 1.0F, 2.0F};
|
|
const RegisterVector registers = pose_to_register_vector(source);
|
|
|
|
test_support::require_equal(registers.size(), static_cast<std::size_t>(14U),
|
|
"pose must occupy 14 registers");
|
|
test_support::require_equal(registers[0], static_cast<std::uint16_t>(0x0000),
|
|
"timestamp high word must be big-endian");
|
|
test_support::require_equal(registers[1], static_cast<std::uint16_t>(0x03E8),
|
|
"timestamp low word must be big-endian");
|
|
test_support::require_equal(registers[2], static_cast<std::uint16_t>(0x3F80),
|
|
"float 1.0 high word must be big-endian");
|
|
test_support::require_equal(registers[3], static_cast<std::uint16_t>(0x0000),
|
|
"float 1.0 low word must be big-endian");
|
|
|
|
const auto decoded = pose_from_register_vector(registers);
|
|
test_support::require_true(decoded.has_value(), "pose decode must succeed");
|
|
test_support::require_equal(decoded->timestamp_ms, 1000U,
|
|
"timestamp must round-trip");
|
|
test_support::require_float_close(decoded->x, 1.0F, "x must round-trip");
|
|
test_support::require_float_close(decoded->y, 2.0F, "y must round-trip");
|
|
test_support::require_float_close(decoded->z, 3.0F, "z must round-trip");
|
|
test_support::require_float_close(decoded->a, 0.0F, "a must round-trip");
|
|
test_support::require_float_close(decoded->b, 1.0F, "b must round-trip");
|
|
test_support::require_float_close(decoded->c, 2.0F, "c must round-trip");
|
|
}
|
|
|
|
void test_parser_rejects_bad_crc() {
|
|
ByteVector frame = build_read_request(kDefaultSlaveId, kDeviceStateAddress, 1U);
|
|
frame.back() ^= 0x01U;
|
|
|
|
const auto parsed = parse_read_request(frame);
|
|
test_support::require_true(!parsed.ok(), "read request parser must reject bad CRC");
|
|
}
|
|
|
|
void test_write_request_parser() {
|
|
const ByteVector frame =
|
|
build_write_request(kDefaultSlaveId, kCorrectionAddress,
|
|
pose_to_register_vector(
|
|
Pose6D{1000U, 1.0F, 2.0F, 3.0F, 0.0F, 1.0F, 2.0F}));
|
|
|
|
const auto parsed = parse_write_request(frame);
|
|
test_support::require_true(parsed.ok(), "write request parser must accept valid frame");
|
|
test_support::require_equal(parsed.value->slave_id, kDefaultSlaveId,
|
|
"write request slave id must parse");
|
|
test_support::require_equal(parsed.value->start_address, kCorrectionAddress,
|
|
"write request start address must parse");
|
|
test_support::require_equal(parsed.value->registers.size(),
|
|
static_cast<std::size_t>(kPoseRegisterCount),
|
|
"write request register count must parse");
|
|
}
|
|
|
|
} // 匿名命名空间
|
|
|
|
int main() {
|
|
test_documented_read_frames();
|
|
test_documented_mode_write_frames();
|
|
test_pose_register_round_trip();
|
|
test_parser_rejects_bad_crc();
|
|
test_write_request_parser();
|
|
|
|
std::cout << "protocol_tests passed\n";
|
|
return 0;
|
|
}
|