// 本文件测试下位机/从站侧服务。验证寄存器表读写、完整请求到响应处理、 // 模式切换带来的状态副作用,以及急停后非法模式切换会被拒绝。 #include "line_laser_modbus/device.hpp" #include "line_laser_modbus/host.hpp" #include "test_support.hpp" #include using namespace line_laser_modbus; namespace { void test_read_status_request_round_trip() { DeviceServer device; HostClient host; const ByteVector response = device.process_request(host.make_read_status_request()); const auto status = host.parse_status_response(response); test_support::require_true(status.has_value(), "device must answer status read"); test_support::require_true(status->mode == WorkMode::StandbyReset, "initial device mode must be standby"); test_support::require_true(status->state == DeviceState::StandbyReady, "initial device state must be ready"); } void test_write_mode_changes_state() { DeviceServer device; HostClient host; const ByteVector ack = device.process_request(host.make_write_mode_request(WorkMode::OnlineTracking)); test_support::require_true( host.parse_write_ack(ack, kModeCommandAddress, 1U), "device must ack valid online tracking mode write"); test_support::require_true(device.bank().mode() == WorkMode::OnlineTracking, "device mode register must update"); test_support::require_true(device.bank().state() == DeviceState::OnlineTrackingNormal, "device state must reflect online tracking"); } void test_emergency_allows_only_standby_exit() { DeviceServer device; HostClient host; const ByteVector emergency_ack = device.process_request(host.make_write_mode_request(WorkMode::EmergencyStop)); test_support::require_true(host.parse_write_ack(emergency_ack, kModeCommandAddress, 1U), "emergency mode write must be accepted"); const ByteVector rejected = device.process_request(host.make_write_mode_request(WorkMode::OnlineTracking)); test_support::require_true(rejected.size() == 5U, "illegal transition must return exception frame"); test_support::require_equal(rejected[1], static_cast(0x90), "write exception function code must be 0x90"); test_support::require_true(device.bank().mode() == WorkMode::EmergencyStop, "illegal transition must not modify mode"); const ByteVector accepted = device.process_request(host.make_write_mode_request(WorkMode::StandbyReset)); test_support::require_true(host.parse_write_ack(accepted, kModeCommandAddress, 1U), "standby exit from emergency must be accepted"); } void test_target_pose_write_and_current_pose_read() { DeviceServer device; HostClient host; const Pose6D target{2000U, 10.0F, 20.0F, 30.0F, 1.0F, 2.0F, 3.0F}; const ByteVector write_ack = device.process_request(host.make_write_target_pose_request(target)); test_support::require_true( host.parse_write_ack(write_ack, kTargetPoseAddress, kPoseRegisterCount), "device must ack target pose write"); const auto stored_target = device.bank().target_pose(); test_support::require_true(stored_target.has_value(), "device must store target pose registers"); test_support::require_equal(stored_target->timestamp_ms, 2000U, "target timestamp must store"); test_support::require_float_close(stored_target->z, 30.0F, "target z must store"); device.bank().set_current_pose(target); const auto read_response = device.process_request(host.make_read_current_pose_request()); const auto current = host.parse_current_pose_response(read_response); test_support::require_true(current.has_value(), "host must parse device pose read"); test_support::require_float_close(current->x, 10.0F, "current x must read"); } void test_out_of_range_read_returns_exception() { DeviceServer device; const ByteVector request = build_read_request(kDefaultSlaveId, static_cast(0xD06C), 1U); const ByteVector response = device.process_request(request); test_support::require_true(response.size() == 5U, "out-of-range read must return exception frame"); test_support::require_equal(response[1], static_cast(0x83), "read exception function code must be 0x83"); } } // 匿名命名空间 int main() { test_read_status_request_round_trip(); test_write_mode_changes_state(); test_emergency_allows_only_standby_exit(); test_target_pose_write_and_current_pose_read(); test_out_of_range_read_returns_exception(); std::cout << "device_tests passed\n"; return 0; }