Files
2026-06-09 12:42:15 +08:00

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;
}