#pragma once #include "line_laser_modbus/protocol.hpp" #include namespace line_laser_modbus { constexpr std::size_t kRegisterBankSize = static_cast(kLastMappedAddress - kModeCommandAddress + 1U); class RegisterBank { public: RegisterBank(); // 地址检查集中在这里处理。V1.2 寄存器表从 0xD000 到 0xD06B 是连续区域, // 包含预留寄存器,因此紧凑数组比 map 更快、更省内存,同时保留后续启用 // 预留寄存器的空间。 [[nodiscard]] bool contains(std::uint16_t start_address, std::uint16_t quantity) const noexcept; [[nodiscard]] std::optional read( std::uint16_t start_address, std::uint16_t quantity) const; [[nodiscard]] bool write(std::uint16_t start_address, const RegisterVector& registers); [[nodiscard]] WorkMode mode() const noexcept; [[nodiscard]] DeviceState state() const noexcept; bool set_mode(WorkMode mode) noexcept; void set_state(DeviceState state) noexcept; void set_current_pose(const Pose6D& pose); [[nodiscard]] std::optional current_pose() const; [[nodiscard]] std::optional target_pose() const; [[nodiscard]] std::optional correction() const; private: [[nodiscard]] static std::size_t index_of(std::uint16_t address) noexcept; void apply_mode_side_effects(WorkMode mode) noexcept; std::array registers_{}; }; // DeviceServer 是下位机/从站侧帧处理器。 // // 固件应在 UART 收到一个完整 RTU 帧后再调用 process_request()。该类会校验 // CRC、功能码、地址范围和模式切换规则,然后返回可直接发送的完整响应帧。 // 如果返回空字节数组,表示该帧应被忽略;在共享 RS-485 总线上,错误从站 // 地址或 CRC 损坏的流量都属于这种情况。 class DeviceServer { public: explicit DeviceServer(std::uint8_t slave_id = kDefaultSlaveId); [[nodiscard]] std::uint8_t slave_id() const noexcept; [[nodiscard]] RegisterBank& bank() noexcept; [[nodiscard]] const RegisterBank& bank() const noexcept; [[nodiscard]] ByteVector process_request(const ByteVector& request); private: [[nodiscard]] ByteVector process_read_request(const ReadRequest& request) const; [[nodiscard]] ByteVector process_write_request(const WriteRequest& request); [[nodiscard]] bool mode_write_is_valid(const WriteRequest& request) const noexcept; std::uint8_t slave_id_; RegisterBank bank_; }; } // 命名空间 line_laser_modbus