// OLEI 2D lidars over UDP (Family A / B / C protocols) — plugin-private header. #pragma once #include "lidar_interface.hpp" #include #include namespace xlidar { // Model presets. Family B/C packets carry enough to auto-detect the model; // Family A doesn't, so MODEL_AUTO keeps the wide default FOV. inline constexpr ModelConfig MODEL_VB { "VB", -135.f, 135.f, 0.05f, 30.f }; // 2D 270° inline constexpr ModelConfig MODEL_VF { "VF", -180.f, 180.f, 0.05f, 30.f }; // 2D 360° inline constexpr ModelConfig MODEL_LR1F { "LR-1F", -180.f, 180.f, 0.05f, 50.f, 180.f }; // 2D 360° 50m; device 0° = rear inline constexpr ModelConfig MODEL_LR1FMI { "LR-1FMI", -180.f, 180.f, 0.05f, 30.f, 180.f }; // 2D 360° (Family B); device 0° = rear inline constexpr ModelConfig MODEL_LR1BS5 { "LR-1BS5", -180.f, 180.f, 0.05f, 30.f }; // 2D 360° (Family B) inline constexpr ModelConfig MODEL_LR16F { "LR-16F", -135.f, 135.f, 0.05f, 30.f }; // 3D 16 line inline constexpr ModelConfig MODEL_GS15 { "GS1-5", -180.f, 180.f, 0.05f, 30.f }; // 2D 360° (Family C/V3) inline constexpr ModelConfig MODEL_AUTO { "AUTO", -180.f, 180.f, 0.05f, 30.f }; // OLEI UDP driver. class OleiDriver : public LidarDriverInterface { public: // ip: local bind address; port: UDP port the lidar sends to; // inverted: unit mounted upside-down → mirror every angle. explicit OleiDriver(const ModelConfig& cfg, const std::string& ip = "0.0.0.0", uint16_t port = 2368, bool inverted = false); ~OleiDriver(); OleiDriver(const OleiDriver&) = delete; OleiDriver& operator=(const OleiDriver&) = delete; DriverInfo get_driver_info() const override; ErrorCode open() override; void close() override; bool recv_scan(ScanResult& out, int timeout_ms = 1000) override; void set_scan_callback(ScanCallback cb) override { cb_ = std::move(cb); } bool spin_once() override; bool is_open() const override { return sock_fd_ >= 0; } // Model name read from the Family B/C header; "AUTO" until one is seen. const char* detected_model() const override { return detected_model_name_.c_str(); } Diagnostics get_diagnostics() const override { return latest_diag_; } private: bool poll_packet(); // one recvfrom() + dispatch to the family parser bool parse_family_a(const uint8_t* buf, int len); // ID=0xFAF0 bool parse_family_b(const uint8_t* buf, int len); // ID=0xFEF0 bool parse_family_c(const uint8_t* buf, int len); // Magic=0xFEAC (GS1-5) void push_point(float signed_angle_deg, float dist_m, uint8_t intensity); void flush_scan(); ModelConfig cfg_; std::string ip_; uint16_t port_; bool inverted_ = false; int sock_fd_ = -1; ScanCallback cb_; // Per-revolution accumulation buffers (index-aligned) std::vector pending_angle_deg_; std::vector pending_dist_m_; std::vector pending_intensity_; uint32_t pending_ts_ = 0; uint8_t pending_err_ = 0; float last_angle_ = -1.f; // wrap detection, device space [0,360) ExtraInfo pending_info_; // Snapshot for get_diagnostics(); refreshed by flush_scan(). Diagnostics latest_diag_; ScanResult ready_result_; bool scan_ready_ = false; // Per-instance so two drivers on two threads don't race. uint8_t recv_buf_[4096]; bool auto_detect_ = false; bool model_locked_ = false; std::string detected_model_name_ = "AUTO"; }; } // namespace xlidar