#include "config.h" #include "json_utils.h" #include #include #include #include #include // ==================== 全局配置实例 ==================== AppConfig cfg; // ==================== 线程级雷达配置绑定 ==================== // 每个雷达一份参数;业务代码用 radar_cfg() 取"当前线程"该用哪一份。 // 采集线程/请求线程/异步保存线程在入口用 RadarScope 绑定。 namespace { thread_local std::shared_ptr tls_rc; // 当前线程绑定的雷达配置 const RadarConfig g_empty_rc; // 无任何雷达时的空配置(全默认) } const RadarConfig& radar_cfg() { if (tls_rc) return *tls_rc; std::shared_ptr f = cfg.first(); return f ? *f : g_empty_rc; } RadarScope::RadarScope(const std::string& radar_id) : prev_(tls_rc) { std::shared_ptr r = cfg.find(radar_id); if (r) tls_rc = r; // 未找到时保持原绑定,不使用默认雷达兜底 } RadarScope::~RadarScope() { tls_rc = prev_; } std::shared_ptr AppConfig::find(const std::string& id) const { if (id.empty()) return first(); auto it = radars.find(id); return (it == radars.end()) ? std::shared_ptr() : it->second; } std::shared_ptr AppConfig::first() const { if (ids.empty()) return std::shared_ptr(); auto it = radars.find(ids.front()); return (it == radars.end()) ? std::shared_ptr() : it->second; } // 解析 "HH:MM[:SS]" -> 时/分/秒;非法返回 false static bool parse_clean_time(const std::string& s, int& h, int& m, int& sec) { int a = -1, b = -1, c = -1; int n = std::sscanf(s.c_str(), "%d:%d:%d", &a, &b, &c); if (n == 2) { h = a; m = b; sec = 0; } else if (n == 3) { h = a; m = b; sec = c; } else return false; return h >= 0 && h <= 23 && m >= 0 && m <= 59 && sec >= 0 && sec <= 59; } // 把一份"单雷达参数对象"解析进 rc(缺项保持默认值)。 // obj 形如 {"radar":{..},"scan":{..},"detect":{..},"count":{..},"filter":{..}} static void parse_radar_sections(const std::string& obj, RadarConfig& rc) { std::string s; double d; bool bv; std::vector> poly; // ---------- radar: 雷达连接与雷达内置滤波 ---------- if (json_get_string(obj, "radar", "ip", s)) rc.radar.ip = s; if (json_get_number(obj, "radar", "port", d)) rc.radar.port = (uint16_t)d; if (json_get_number(obj, "radar", "hz", d)) rc.radar.hz = d; if (json_get_number(obj, "radar", "tail_filter", d)) rc.radar.tail_filter = (int)d; if (json_get_number(obj, "radar", "front_filter", d)) rc.radar.front_filter = (int)d; if (json_get_number(obj, "radar", "far_hr_filter", d)) rc.radar.far_hr_filter = (int)d; if (json_get_bool(obj, "radar", "near_hr_filter", bv)) rc.radar.near_hr_filter = bv; // ---------- scan: 扫描采集与走行 ---------- if (json_get_number(obj, "scan", "collect_duration_s", d)) rc.scan.collect_duration_s = (int)d; if (json_get_number(obj, "scan", "z_speed", d)) rc.scan.z_speed = d; if (json_get_string(obj, "scan", "z_speed_type", s)) { if (s == "configuration" || s == "uniformSpeed") rc.scan.z_speed_type = s; else rc.scan.z_speed_type = "uniformSpeed"; } // ---------- detect: 截面分析 ---------- if (json_get_number(obj, "detect", "z_range_mm", d)) rc.detect.z_range_mm = d; if (json_get_number(obj, "detect", "y_range_mm", d)) rc.detect.y_range_mm = d; if (json_get_number(obj, "detect", "rect_width_px", d)) rc.detect.rect_width_px = (int)d; if (json_get_number(obj, "detect", "rect_length_ratio", d)) rc.detect.rect_length_ratio = d; // ---------- count: 分层计数 ---------- if (json_get_number(obj, "count", "floor_height", d)) rc.count.floor_height = d; if (json_get_number(obj, "count", "base_tolerance", d)) { rc.count.base_tolerance = d; if (rc.count.base_tolerance < 0) rc.count.base_tolerance = 0; } if (json_get_number(obj, "count", "per_level_tolerance", d)) { rc.count.per_level_tolerance = d; if (rc.count.per_level_tolerance < 0) rc.count.per_level_tolerance = 0; } if (json_get_number(obj, "count", "point_dilate", d)) { rc.count.point_dilate = (int)d; if (rc.count.point_dilate < 1) rc.count.point_dilate = 1; if (rc.count.point_dilate > 64) rc.count.point_dilate = 64; } if (json_get_number(obj, "count", "interp_bucket_deg", d)) { // 角度插值桶宽(度)。应等于雷达实际角度分辨率; 越界时回退 0.08 rc.count.interp_bucket_deg = d; if (rc.count.interp_bucket_deg < 0.005) rc.count.interp_bucket_deg = 0.08; if (rc.count.interp_bucket_deg > 5.0) rc.count.interp_bucket_deg = 0.08; } if (json_get_string(obj, "count", "z_interpolate_mode", s)) { if (s == "CopyTranslation" || s == "AngleBucketInterpolation" || s == "AngleBucketInterpolationByDilate") rc.count.z_interpolate_mode = s; else rc.count.z_interpolate_mode = "none"; // 未知拼写一律按不插值处理 } if (json_get_string(obj, "count", "layer_algorithm", s)) { if (s == "HalfMax" || s == "KdePeak") rc.count.layer_algorithm = s; else rc.count.layer_algorithm = "LevelTolerance"; // 非法值回退默认算法 } if (json_get_number(obj, "count", "kde_bandwidth", d)) { rc.count.kde_bandwidth = d; if (rc.count.kde_bandwidth < 0) rc.count.kde_bandwidth = 0; } if (json_get_number(obj, "count", "kde_step", d)) { rc.count.kde_step = d; if (rc.count.kde_step < 0.1) rc.count.kde_step = 0.1; // 步长过小无意义且极慢 if (rc.count.kde_step > 100.0) rc.count.kde_step = 100.0; } // ---------- filter: 多边形过滤 ---------- poly.clear(); if (json_get_point_array(obj, "filter", "polygon", poly) >= 3) { rc.filter.polygon = poly; rc.filter.poly_n = (int)poly.size(); } // ---------- filter.tail_cut ---------- { std::string o = json_get_raw(obj, "filter", "tail_cut"); if (!o.empty()) { if (json_get_bool(o, "enable", bv)) rc.filter.tail_cut.enable = bv; if (json_get_number(o, "max_tail_length", d)) { rc.filter.tail_cut.max_tail_length = (int)d; if (rc.filter.tail_cut.max_tail_length < 0) rc.filter.tail_cut.max_tail_length = 0; } if (json_get_number(o, "y_delta", d)) { rc.filter.tail_cut.y_delta = d; if (rc.filter.tail_cut.y_delta < 0) rc.filter.tail_cut.y_delta = 0; } if (json_get_number(o, "min_run_length", d)) { rc.filter.tail_cut.min_run_length = (int)d; if (rc.filter.tail_cut.min_run_length < 1) rc.filter.tail_cut.min_run_length = 1; } if (json_get_number(o, "core_min_len", d)) { rc.filter.tail_cut.core_min_len = (int)d; if (rc.filter.tail_cut.core_min_len < 0) rc.filter.tail_cut.core_min_len = 0; } if (json_get_number(o, "support_rows", d)) { rc.filter.tail_cut.support_rows = (int)d; if (rc.filter.tail_cut.support_rows < 0) rc.filter.tail_cut.support_rows = 0; if (rc.filter.tail_cut.support_rows > 4) rc.filter.tail_cut.support_rows = 4; } } } // ---------- filter.sparse_cut ---------- { std::string o = json_get_raw(obj, "filter", "sparse_cut"); if (!o.empty()) { if (json_get_bool(o, "enable", bv)) rc.filter.sparse_cut.enable = bv; if (json_get_number(o, "x_radius", d)) { rc.filter.sparse_cut.x_radius = d; if (rc.filter.sparse_cut.x_radius < 0) rc.filter.sparse_cut.x_radius = 0; } if (json_get_number(o, "circle_min", d)) { rc.filter.sparse_cut.circle_min = (int)d; if (rc.filter.sparse_cut.circle_min < 1) rc.filter.sparse_cut.circle_min = 1; } if (json_get_number(o, "run_min", d)) { rc.filter.sparse_cut.run_min = (int)d; if (rc.filter.sparse_cut.run_min < 1) rc.filter.sparse_cut.run_min = 1; } } } } // ==================== 配置加载(根级 map 型 JSON + // 注释行) ==================== // 结构: {"server":{...},"log":{...},"<雷达标识>":{radar/scan/detect/count/filter}, ...} // 兼容: 旧格式(根下直接 radar/scan/... 段)时整体视为一台雷达,标识 "default"。 bool load_config(const std::string& path) { std::ifstream fin(path); if (!fin.is_open()) { // 绝不 exit:运行期 config 命令会调用本函数,exit 会让整个服务进程瞬间消失 std::cerr << "[错误] 未找到配置文件: " << path << " (保留现有配置不变)" << std::endl; return false; } // 1. 剔除行首(忽略空白)以 // 开头的注释行,其余拼接为 JSON 文本 std::ostringstream json_oss; std::string line; while (std::getline(fin, line)) { size_t b = line.find_first_not_of(" \t\r\n"); if (b != std::string::npos && line.compare(b, 2, "//") == 0) continue; // 整行注释 json_oss << line << "\n"; } fin.close(); const std::string text = json_oss.str(); std::string s; double d; bool bv; // 全程解析到临时对象:解析失败/无雷达时不改动正在使用的 cfg, // 避免运行期 config 重载把全局配置清成空导致其它线程访问空 map。 AppConfig new_cfg = cfg; // 2. 全局段: server / log if (json_get_string(text, "server", "mode", s)) new_cfg.server.mode = s; if (json_get_number(text, "server", "port", d)) new_cfg.server.port = (uint16_t)d; if (json_get_bool(text, "log", "enable", bv)) new_cfg.log.enable = bv; if (json_get_number(text, "log", "retain_days", d)) { new_cfg.log.retain_days = (int)d; if (new_cfg.log.retain_days < 1) new_cfg.log.retain_days = 1; } if (json_get_string(text, "log", "clean_time", s)) { int h = -1, m = -1, sec = -1; if (parse_clean_time(s, h, m, sec)) { new_cfg.log.clean_hour = h; new_cfg.log.clean_minute = m; new_cfg.log.clean_sec = sec; } else { std::cout << "[WARN] log.clean_time 格式应为 \"HH:MM:SS\",已忽略: " << s << " (仍用 " << new_cfg.log.clean_hour << ":" << (new_cfg.log.clean_minute < 10 ? "0" : "") << new_cfg.log.clean_minute << ")" << std::endl; } } // 3. 雷达段: 除 server/log 外的一级键都是雷达标识 std::map> new_radars; std::vector new_ids; std::vector keys = json_object_keys(text); // 旧格式判定:根下(一级键)直接有 "radar" 段且带 ip。 // 注意不能直接用 json_get_raw(text,"radar") 判定——它是扁平查找,会命中雷达条目内的 "radar" 子对象。 bool has_top_radar = std::find(keys.begin(), keys.end(), "radar") != keys.end(); std::string radar_raw = has_top_radar ? json_get_raw(text, "radar") : std::string(); bool old_format = !radar_raw.empty() && radar_raw[0] == '{' && json_get_string(radar_raw, "ip", s); if (old_format) { std::cout << "[WARN] 检测到旧格式配置(根级 radar/scan/... 段),已按单雷达 \"default\" 载入;" << "建议改为根级 map: {\"<雷达标识>\":{radar,scan,detect,count,filter}}" << std::endl; std::shared_ptr rc(new RadarConfig()); rc->id = "default"; parse_radar_sections(text, *rc); new_radars[rc->id] = rc; new_ids.push_back(rc->id); } else { for (const auto& k : keys) { if (k == "server" || k == "log") continue; std::string obj = json_get_raw(text, k); if (obj.empty() || obj[0] != '{') continue; // 非对象键忽略 std::shared_ptr rc(new RadarConfig()); rc->id = k; parse_radar_sections(obj, *rc); new_radars[k] = rc; new_ids.push_back(k); } } if (new_ids.empty()) { std::cerr << "[错误] 配置中未找到任何雷达(除 server/log 外需至少一个雷达标识对象)!" << " 保留现有配置不变。" << std::endl; return false; } // 解析成功:整体提交(旧配置由仍在使用的线程通过 shared_ptr 继续持有,不会悬空) new_cfg.radars.swap(new_radars); new_cfg.ids.swap(new_ids); cfg = std::move(new_cfg); // ==================== 打印配置摘要 ==================== std::cout << "========== 配置已加载 (雷达" << cfg.ids.size() << "台) ==========" << std::endl; for (const auto& id : cfg.ids) { const RadarConfig& rc = *cfg.radars[id]; std::cout << "[" << id << "] 雷达: " << rc.radar.ip << ":" << rc.radar.port << " 帧率: " << rc.radar.hz << "Hz" << " 拖尾滤波: " << rc.radar.tail_filter << " 前冲滤波: " << rc.radar.front_filter << std::endl; std::cout << "[" << id << "] 扫描: " << rc.scan.collect_duration_s << "s" << " Z速度: " << rc.scan.z_speed_type << " " << rc.scan.z_speed << "cm/s" << " 地板高: " << rc.count.floor_height << "mm" << " 分层: 基准" << rc.count.base_tolerance << "+每层" << rc.count.per_level_tolerance << " 层数算法: " << rc.count.layer_algorithm << " 插值: " << rc.count.z_interpolate_mode << " 插值桶宽: " << rc.count.interp_bucket_deg << "°" << std::endl; std::cout << "[" << id << "] 多边形(" << rc.filter.poly_n << "点):"; for (const auto& p : rc.filter.polygon) std::cout << " (" << p.first << "," << p.second << ")"; std::cout << " SparseCut=" << (rc.filter.sparse_cut.enable ? "开" : "关") << "(X±" << rc.filter.sparse_cut.x_radius << "mm,同圈>=" << rc.filter.sparse_cut.circle_min << ",跨圈>=" << rc.filter.sparse_cut.run_min << ")" << " TailCut=" << (rc.filter.tail_cut.enable ? "开" : "关") << std::endl; } std::cout << "运行模式: " << cfg.server.mode << (cfg.server.mode == "tcp" || cfg.server.mode == "http" ? " (端口 " + std::to_string(cfg.server.port) + ")" : "") << std::endl; std::cout << "日志保留: " << cfg.log.retain_days << "天" << " 每日清理: " << (cfg.log.clean_hour < 10 ? "0" : "") << cfg.log.clean_hour << ":" << (cfg.log.clean_minute < 10 ? "0" : "") << cfg.log.clean_minute << ":" << (cfg.log.clean_sec < 10 ? "0" : "") << cfg.log.clean_sec << std::endl; std::cout << "================================" << std::endl; return true; }