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

148 lines
5.4 KiB
C++

// 本文件测试板卡运动控制适配层。验证 MotionControlBridge 是否会在处理请求前
// 发布当前位姿/状态,并在合法写模式、目标位姿和纠偏量后调用板卡适配接口。
#include "line_laser_modbus/host.hpp"
#include "line_laser_modbus/motion_adapter.hpp"
#include "test_support.hpp"
#include <iostream>
using namespace line_laser_modbus;
namespace {
class FakeMotion final : public MotionControlAdapter {
public:
void on_mode_changed(const WorkMode mode) override {
last_mode = mode;
++mode_calls;
}
void on_target_pose(const Pose6D& pose) override {
last_target = pose;
++target_calls;
}
void on_correction(const Pose6D& correction) override {
last_correction = correction;
++correction_calls;
}
[[nodiscard]] Pose6D current_pose() const override {
return pose;
}
[[nodiscard]] DeviceState current_state() const override {
return state;
}
Pose6D pose{123U, 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F};
DeviceState state = DeviceState::OnlineTrackingNormal;
WorkMode last_mode = WorkMode::StandbyReset;
Pose6D last_target{};
Pose6D last_correction{};
int mode_calls = 0;
int target_calls = 0;
int correction_calls = 0;
};
void test_bridge_publishes_feedback_before_read() {
HostClient host;
DeviceServer server;
FakeMotion motion;
MotionControlBridge bridge(server, motion);
const auto response = bridge.process_request(host.make_read_current_pose_request());
const auto parsed_pose = host.parse_current_pose_response(response);
test_support::require_true(parsed_pose.has_value(),
"bridge must return current pose response");
test_support::require_equal(parsed_pose->timestamp_ms, 123U,
"bridge must publish adapter timestamp");
test_support::require_float_close(parsed_pose->x, 1.0F,
"bridge must publish adapter pose x");
}
void test_bridge_notifies_mode_after_valid_write() {
HostClient host;
DeviceServer server;
FakeMotion motion;
MotionControlBridge bridge(server, motion);
const auto response =
bridge.process_request(host.make_write_mode_request(WorkMode::OnlineTracking));
test_support::require_true(host.parse_write_ack(response, kModeCommandAddress, 1U),
"valid mode write must be acknowledged");
test_support::require_equal(motion.mode_calls, 1,
"bridge must notify mode write once");
test_support::require_true(motion.last_mode == WorkMode::OnlineTracking,
"bridge must pass written mode to adapter");
}
void test_bridge_notifies_pose_writes() {
HostClient host;
DeviceServer server;
FakeMotion motion;
MotionControlBridge bridge(server, motion);
const Pose6D target{200U, 10.0F, 20.0F, 30.0F, 1.0F, 2.0F, 3.0F};
const auto target_response =
bridge.process_request(host.make_write_target_pose_request(target));
test_support::require_true(
host.parse_write_ack(target_response, kTargetPoseAddress, kPoseRegisterCount),
"target pose write must be acknowledged");
test_support::require_equal(motion.target_calls, 1,
"bridge must notify target pose write");
test_support::require_equal(motion.last_target.timestamp_ms, 200U,
"bridge must pass target timestamp");
test_support::require_float_close(motion.last_target.z, 30.0F,
"bridge must pass target z");
const Pose6D correction{201U, 0.5F, -0.25F, 1.5F, 0.0F, 0.0F, 0.0F};
const auto correction_response =
bridge.process_request(host.make_write_correction_request(correction));
test_support::require_true(
host.parse_write_ack(correction_response, kCorrectionAddress,
kPoseRegisterCount),
"correction write must be acknowledged");
test_support::require_equal(motion.correction_calls, 1,
"bridge must notify correction write");
test_support::require_float_close(motion.last_correction.y, -0.25F,
"bridge must pass correction y");
}
void test_bridge_does_not_notify_rejected_mode_write() {
HostClient host;
DeviceServer server;
FakeMotion motion;
MotionControlBridge bridge(server, motion);
const auto emergency_response =
bridge.process_request(host.make_write_mode_request(WorkMode::EmergencyStop));
test_support::require_true(
host.parse_write_ack(emergency_response, kModeCommandAddress, 1U),
"emergency mode write must be acknowledged");
const auto rejected =
bridge.process_request(host.make_write_mode_request(WorkMode::OnlineTracking));
test_support::require_true(!host.parse_write_ack(rejected, kModeCommandAddress, 1U),
"illegal mode transition must not be acknowledged");
test_support::require_equal(motion.mode_calls, 1,
"bridge must not notify rejected mode write");
test_support::require_true(motion.last_mode == WorkMode::EmergencyStop,
"last notified mode must remain emergency");
}
} // 匿名命名空间
int main() {
test_bridge_publishes_feedback_before_read();
test_bridge_notifies_mode_after_valid_write();
test_bridge_notifies_pose_writes();
test_bridge_does_not_notify_rejected_mode_write();
std::cout << "adapter_tests passed\n";
return 0;
}