feat(config,diagnostics): explicit transport in DeviceConfig, vendor-neutral diagnostics
DeviceConfig now carries an optional transport (serial/udp/tcp) instead of
the ESPE-only use_udp bool. Plugins validate it in create_driver_instance:
a fixed-transport driver configured with the wrong transport fails open()
with InvalidConfig (via InvalidConfigDriver — the plugin ABI forbids
returning nullptr) rather than silently ignoring the setting. Selectable
drivers (ESPE) switch TCP/UDP through the same field. config.json
load/save round-trips "transport" for every transport, including serial,
and migrates legacy use_udp:true entries.
Diagnostics drops the per-vendor accessors (espe_fault, rplidar_fault,
monitor_fault, sick_error, pollution_*, contamination_*, manipulation) for
one common shape: a list of DiagnosticIssue{severity, code, detail} with
cross-vendor codes, plus a raw map of vendor passthrough values and
to_json() for hosts that prefer a string. Vendor bit decoding now lives in
one place (decode_diagnostics); has_fault/has_warning/healthy keep their
meaning, so is_ready()/wait_ready() are unchanged.
Also: README regains the model/protocol and ExtraInfo tables lost in the
lidarlib->xlidar refactor (verified against current code), and the empty
xlocd/ tree left by a stray sync run is gone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,18 +8,65 @@
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <limits>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <string>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <utility>
|
||||
|
||||
namespace xlidar {
|
||||
|
||||
inline constexpr float kDeg2Rad = 3.14159265358979323846f / 180.f;
|
||||
|
||||
// True when the requested DeviceConfig::transport is one this driver can
|
||||
// serve: unset always matches (driver default); otherwise the declared
|
||||
// transport, or — for transport-selectable drivers — either side of the
|
||||
// TCP/UDP pair.
|
||||
inline bool transport_supported(const DriverInfo& info, const DeviceConfig& cfg) {
|
||||
if (!cfg.transport || *cfg.transport == info.transport) return true;
|
||||
if (info.transport_selectable)
|
||||
return (*cfg.transport == Transport::Udp && info.transport == Transport::Tcp) ||
|
||||
(*cfg.transport == Transport::Tcp && info.transport == Transport::Udp);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stand-in returned by create_driver_instance() when a structurally valid
|
||||
// config still can't be served (e.g. transport mismatch): the plugin ABI
|
||||
// forbids returning nullptr there, so the error surfaces from open() as
|
||||
// InvalidConfig instead of the setting being silently ignored.
|
||||
class InvalidConfigDriver : public LidarDriverInterface {
|
||||
public:
|
||||
InvalidConfigDriver(DriverInfo info, std::string reason)
|
||||
: info_(std::move(info)), reason_(std::move(reason)) {}
|
||||
|
||||
DriverInfo get_driver_info() const override { return info_; }
|
||||
ErrorCode open() override {
|
||||
std::fprintf(stderr, "[xlidar] %s: %s\n", info_.driver_id.c_str(), reason_.c_str());
|
||||
return set_error(ErrorCode::InvalidConfig);
|
||||
}
|
||||
void close() override {}
|
||||
bool recv_scan(ScanResult&, int) override {
|
||||
set_error(ErrorCode::NotOpen);
|
||||
return false;
|
||||
}
|
||||
void set_scan_callback(ScanCallback) override {}
|
||||
bool spin_once() override {
|
||||
set_error(ErrorCode::NotOpen);
|
||||
return false;
|
||||
}
|
||||
const char* detected_model() const override { return info_.model.c_str(); }
|
||||
bool is_open() const override { return false; }
|
||||
|
||||
private:
|
||||
DriverInfo info_;
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
// Remap a finished scan's angular window onto [min_deg, max_deg]. Only
|
||||
// angle_min/angle_max/angle_increment are rewritten; points are untouched.
|
||||
inline void remap_scan_window(LaserScan& scan, float min_deg, float max_deg) {
|
||||
|
||||
@@ -270,12 +270,13 @@ const DriverInfo kDriverInfo = [] {
|
||||
info.model = "LGA60";
|
||||
info.driver_id = "espe_lga60_driver";
|
||||
info.description = "ESPE LGA60 320° laser scanner — TCP by default, UDP via "
|
||||
"use_udp; open() sends the RAuto start command; device "
|
||||
"parameters come from the vendor Windows tool. Default "
|
||||
"port 8080 (vendor default IP 192.168.1.88). Ported from "
|
||||
"the vendor ROS driver; not verified on real hardware.";
|
||||
"DeviceConfig::transport; open() sends the RAuto start "
|
||||
"command; device parameters come from the vendor Windows "
|
||||
"tool. Default port 8080 (vendor default IP 192.168.1.88). "
|
||||
"Ported from the vendor ROS driver; not verified on real "
|
||||
"hardware.";
|
||||
info.transport = Transport::Tcp;
|
||||
info.transport_selectable = true; // use_udp switches to UDP
|
||||
info.transport_selectable = true; // transport = udp switches to UDP
|
||||
info.supported_models = {"ESPE-LGA60"};
|
||||
return info;
|
||||
}();
|
||||
@@ -293,7 +294,11 @@ XLIDAR_PLUGIN_EXPORT void get_driver_info(xlidar::DriverInfo* out) {
|
||||
XLIDAR_PLUGIN_EXPORT xlidar::LidarDriverInterface*
|
||||
create_driver_instance(const xlidar::DeviceConfig* cfg) {
|
||||
using namespace xlidar;
|
||||
const uint16_t port = cfg->port ? cfg->port : 8080;
|
||||
if (!transport_supported(kDriverInfo, *cfg))
|
||||
return new InvalidConfigDriver(kDriverInfo,
|
||||
std::string("unsupported transport '") + to_string(*cfg->transport) + "'");
|
||||
const uint16_t port = cfg->port ? cfg->port : 8080;
|
||||
const bool use_udp = cfg->transport == Transport::Udp;
|
||||
return new EspeDriver(apply_device_config(MODEL_ESPE_LGA60, *cfg),
|
||||
cfg->ip, port, cfg->use_udp, cfg->inverted);
|
||||
cfg->ip, port, use_udp, cfg->inverted);
|
||||
}
|
||||
|
||||
@@ -450,6 +450,9 @@ XLIDAR_PLUGIN_EXPORT void get_driver_info(xlidar::DriverInfo* out) {
|
||||
XLIDAR_PLUGIN_EXPORT xlidar::LidarDriverInterface*
|
||||
create_driver_instance(const xlidar::DeviceConfig* cfg) {
|
||||
using namespace xlidar;
|
||||
if (!transport_supported(kDriverInfo, *cfg))
|
||||
return new InvalidConfigDriver(kDriverInfo,
|
||||
std::string("unsupported transport '") + to_string(*cfg->transport) + "'");
|
||||
const ModelConfig* preset = model_by_name(cfg->model);
|
||||
if (!preset) preset = &MODEL_AUTO; // unknown model → auto-detect
|
||||
const uint16_t port = cfg->port ? cfg->port : 2368;
|
||||
|
||||
@@ -318,6 +318,9 @@ XLIDAR_PLUGIN_EXPORT void get_driver_info(xlidar::DriverInfo* out) {
|
||||
XLIDAR_PLUGIN_EXPORT xlidar::LidarDriverInterface*
|
||||
create_driver_instance(const xlidar::DeviceConfig* cfg) {
|
||||
using namespace xlidar;
|
||||
if (!transport_supported(kDriverInfo, *cfg))
|
||||
return new InvalidConfigDriver(kDriverInfo,
|
||||
std::string("unsupported transport '") + to_string(*cfg->transport) + "'");
|
||||
// "AUTO" and "C1" share the same preset; the real model is read from the
|
||||
// device at open().
|
||||
const uint32_t baud = cfg->baudrate ? cfg->baudrate : 460800;
|
||||
|
||||
@@ -255,6 +255,9 @@ XLIDAR_PLUGIN_EXPORT void get_driver_info(xlidar::DriverInfo* out) {
|
||||
XLIDAR_PLUGIN_EXPORT xlidar::LidarDriverInterface*
|
||||
create_driver_instance(const xlidar::DeviceConfig* cfg) {
|
||||
using namespace xlidar;
|
||||
if (!transport_supported(kDriverInfo, *cfg))
|
||||
return new InvalidConfigDriver(kDriverInfo,
|
||||
std::string("unsupported transport '") + to_string(*cfg->transport) + "'");
|
||||
const uint16_t port = cfg->port ? cfg->port : 6060;
|
||||
return new SickSafetyDriver(apply_device_config(MODEL_SICK_NANOSCAN3, *cfg),
|
||||
cfg->ip, port, cfg->inverted);
|
||||
|
||||
@@ -312,6 +312,9 @@ XLIDAR_PLUGIN_EXPORT void get_driver_info(xlidar::DriverInfo* out) {
|
||||
XLIDAR_PLUGIN_EXPORT xlidar::LidarDriverInterface*
|
||||
create_driver_instance(const xlidar::DeviceConfig* cfg) {
|
||||
using namespace xlidar;
|
||||
if (!transport_supported(kDriverInfo, *cfg))
|
||||
return new InvalidConfigDriver(kDriverInfo,
|
||||
std::string("unsupported transport '") + to_string(*cfg->transport) + "'");
|
||||
const ModelConfig* preset = model_by_name(cfg->model);
|
||||
if (!preset) preset = &MODEL_SICK_TIM571; // brand default
|
||||
const uint16_t port = cfg->port ? cfg->port : 2111;
|
||||
|
||||
Reference in New Issue
Block a user