|
@@ -0,0 +1,1005 @@
|
|
|
|
|
+# PlateRecApp 技术方案文档
|
|
|
|
|
+
|
|
|
|
|
+> **版本**: v43.2 fix24
|
|
|
|
|
+> **作者**: steven_roc
|
|
|
|
|
+> **仓库**: https://git.zhonjin.com:40717/steven_roc/PlateRecApp
|
|
|
|
|
+> **最后更新**: 2026-07-06
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 1. 项目概述
|
|
|
|
|
+
|
|
|
|
|
+### 1.1 项目名称
|
|
|
|
|
+**PlateRecApp**(HyperLPR3-LinuxAPP)
|
|
|
|
|
+
|
|
|
|
|
+### 1.2 系统定位
|
|
|
|
|
+面向渣土车/工程车辆管理的**车牌识别+称重+联单上传**一体化边缘计算系统。部署在工地出入口的树莓派5上,通过4路RTSP摄像头实时捕获车牌,结合称重仪表数据,自动完成:
|
|
|
|
|
+
|
|
|
|
|
+- 车牌识别(HyperLPR3 + MNN推理引擎)
|
|
|
|
|
+- 进站/出站交替锁定管理
|
|
|
|
|
+- 照片水印叠加与平台上传
|
|
|
|
|
+- 飞书实时通知推送
|
|
|
|
|
+- MQTT消息发布
|
|
|
|
|
+- 耀华称重仪表数据采集与上报
|
|
|
|
|
+- Web管理后台(监控/视频/配置/锁定管理)
|
|
|
|
|
+- frpc内网穿透管理
|
|
|
|
|
+- SSH密钥管理
|
|
|
|
|
+- 系统资源监控
|
|
|
|
|
+
|
|
|
|
|
+### 1.3 目标场景
|
|
|
|
|
+工地/码头/回填点出入口,单点双向(进站+出站),支持4个摄像头(前低位×2 + 侧高位×2),支持共享摄像头模式。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 2. 软件环境
|
|
|
|
|
+
|
|
|
|
|
+### 2.1 目标硬件
|
|
|
|
|
+| 参数 | 规格 |
|
|
|
|
|
+|------|------|
|
|
|
|
|
+| 主板 | 树莓派5 8GB |
|
|
|
|
|
+| CPU | BCM2712, Quad-core Arm Cortex-A76 |
|
|
|
|
|
+| GPU | VideoCore VII |
|
|
|
|
|
+| 内存 | 8GB LPDDR4X |
|
|
|
|
|
+
|
|
|
|
|
+### 2.2 操作系统
|
|
|
|
|
+| 参数 | 值 |
|
|
|
|
|
+|------|-----|
|
|
|
|
|
+| OS | Ubuntu 24.04 LTS |
|
|
|
|
|
+| 架构 | aarch64 (ARM64) |
|
|
|
|
|
+| 内核 | Linux 6.x |
|
|
|
|
|
+
|
|
|
|
|
+### 2.3 编译器与构建工具
|
|
|
|
|
+| 工具 | 版本/标准 |
|
|
|
|
|
+|------|-----------|
|
|
|
|
|
+| GCC | 11+ (aarch64-linux-gnu) |
|
|
|
|
|
+| C++ 标准 | C++17 (CMAKE_CXX_STANDARD 17) |
|
|
|
|
|
+| CMake | 3.25.1+ |
|
|
|
|
|
+| 构建类型 | Release (默认) / Debug / MinSizeRel / RelWithDebInfo |
|
|
|
|
|
+
|
|
|
|
|
+### 2.4 依赖库清单
|
|
|
|
|
+
|
|
|
|
|
+| 库名 | 用途 | 查找方式 |
|
|
|
|
|
+|------|------|----------|
|
|
|
|
|
+| OpenCV | 图像处理、JPEG编解码、水印渲染 | `find_package(OpenCV REQUIRED)` |
|
|
|
|
|
+| HyperLPR3 SDK | 车牌检测+识别(MNN推理) | 头文件 `hyper_lpr_sdk.h`,库 `libhyperlpr3` |
|
|
|
|
|
+| SQLite3 | 本地数据库(记录存储、认证、监控) | `pkg_check_modules(SQLITE3 REQUIRED sqlite3)` |
|
|
|
|
|
+| cURL | HTTP客户端(API上传、飞书消息) | `find_package(CURL REQUIRED)` |
|
|
|
|
|
+| OpenSSL | HTTPS/SSL、MD5计算 | `find_package(OpenSSL REQUIRED)` |
|
|
|
|
|
+| FreeType2 | 中文字体渲染(水印文字) | `pkg_check_modules(FREETYPE REQUIRED freetype2)` |
|
|
|
|
|
+| FFmpeg (libavformat/libavcodec/libavutil/libswscale) | RTSP流解码、DRM硬件加速 | `pkg_check_modules` |
|
|
|
|
|
+| cJSON | JSON解析与生成 | 手动查找 + pkg-config 回退 |
|
|
|
|
|
+| mosquitto / mosquittopp | MQTT客户端通信 | 直接链接 |
|
|
|
|
|
+| libcrypt | bcrypt密码哈希(`crypt_r`) | `find_library(CRYPT_LIBRARY crypt)` |
|
|
|
|
|
+| inih | INI配置文件解析 | 第三方头文件 `ini.h` |
|
|
|
|
|
+| httplib (cpp-httplib) | 轻量级HTTP/HTTPS服务器 | 第三方头文件 `httplib.h` |
|
|
|
|
|
+| cvxFont | OpenCV中文字体绘制 | 第三方库 `cvxFont.h` |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 3. 系统架构
|
|
|
|
|
+
|
|
|
|
|
+### 3.1 模块划分图
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+┌─────────────────────────────────────────────────────────────────────┐
|
|
|
|
|
+│ main.cpp (主调度) │
|
|
|
|
|
+│ 配置加载 → 数据库初始化 → HyperLPR初始化 → 摄像头启动 → 主循环 │
|
|
|
|
|
+└──────┬──────────┬──────────┬──────────┬──────────┬─────────────────┘
|
|
|
|
|
+ │ │ │ │ │
|
|
|
|
|
+ ┌────▼───┐ ┌───▼────┐ ┌──▼───┐ ┌───▼────┐ ┌───▼──────┐
|
|
|
|
|
+ │ config │ │database│ │web │ │feishu │ │ MQTT + │
|
|
|
|
|
+ │ .cpp │ │ .cpp │ │server│ │client │ │ Weight │
|
|
|
|
|
+ └────────┘ └────────┘ └──────┘ └────────┘ │ Scale │
|
|
|
|
|
+ └──────────┘
|
|
|
|
|
+ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐
|
|
|
|
|
+ │ RTSP │ │ Plate │ │ Station │ │ Network │
|
|
|
|
|
+ │ Capture │ │Recognize │ │ Lock │ │ Client │
|
|
|
|
|
+ │ (×4) │ │ r.cpp │ │ .cpp │ │ .cpp │
|
|
|
|
|
+ └──────────┘ └──────────┘ └──────────┘ └──────────────┘
|
|
|
|
|
+ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐
|
|
|
|
|
+ │ Auth │ │ frpc │ │ SSH │ │ System │
|
|
|
|
|
+ │ System │ │ Manager │ │ Manager │ │ Monitor │
|
|
|
|
|
+ │(*.cpp) │ │ .cpp │ │ .cpp │ │ .cpp │
|
|
|
|
|
+ └──────────┘ └──────────┘ └──────────┘ └──────────────┘
|
|
|
|
|
+ ┌──────────┐ ┌──────────────────────────────────────────┐
|
|
|
|
|
+ │ Log │ │ common.h (全局类型/结构体/指标) │
|
|
|
|
|
+ │ Manager │ │ MonitorMetrics / ROIConfig / ... │
|
|
|
|
|
+ └──────────┘ └──────────────────────────────────────────┘
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 模块间依赖关系
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+common.h ← 所有模块(全局类型定义)
|
|
|
|
|
+config.cpp ← main.cpp(配置加载)
|
|
|
|
|
+rtsp_capture.cpp ← plate_recognizer.cpp(帧采集)
|
|
|
|
|
+plate_recognizer.cpp ← main.cpp(识别主逻辑)
|
|
|
|
|
+station_lock.cpp ← plate_recognizer.cpp(交替锁定)
|
|
|
|
|
+database.cpp ← plate_recognizer.cpp, web_server.cpp(数据持久化)
|
|
|
|
|
+network_client.cpp ← plate_recognizer.cpp, weight_scale.cpp(HTTP API)
|
|
|
|
|
+feishu_client.cpp ← plate_recognizer.cpp(飞书通知)
|
|
|
|
|
+mqtt_client.cpp ← weight_scale.cpp(MQTT发布)
|
|
|
|
|
+weight_scale.cpp ← main.cpp(称重数据采集)
|
|
|
|
|
+web_server.cpp ← main.cpp(Web服务线程)
|
|
|
|
|
+auth_*.cpp ← web_server.cpp(认证中间件)
|
|
|
|
|
+frpc_manager.cpp ← web_server.cpp(frpc管理API)
|
|
|
|
|
+ssh_manager.cpp ← web_server.cpp(SSH密钥管理)
|
|
|
|
|
+log_manager.cpp ← main.cpp(日志缓冲)
|
|
|
|
|
+system_monitor.cpp ← web_server.cpp(系统监控)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3.3 数据流图
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+RTSP摄像头(×4)
|
|
|
|
|
+ │
|
|
|
|
|
+ ▼ FFmpeg解码(软解/DRM硬解)
|
|
|
|
|
+帧队列(deque<cv::Mat>, 最大10帧)
|
|
|
|
|
+ │
|
|
|
|
|
+ ▼ getFrame() 取出最新帧
|
|
|
|
|
+ROI裁剪(可选, 按配置区域)
|
|
|
|
|
+ │
|
|
|
|
|
+ ▼ HyperLPR3推理(HLPR_UpdateImage)
|
|
|
|
|
+车牌检测结果(车牌号+置信度+类型)
|
|
|
|
|
+ │
|
|
|
|
|
+ ▼ 帧级去重(2秒内同车牌跳过)
|
|
|
|
|
+ │
|
|
|
|
|
+ ├─── 置信度 ≥ LOG_THRESHOLD? ──→ 保存照片(加水印) → 插入SQLite
|
|
|
|
|
+ │
|
|
|
|
|
+ ▼ 置信度 ≥ CONFIDENCE_THRESHOLD?
|
|
|
|
|
+ │
|
|
|
|
|
+ ├─── 交替锁定检查(try_lock_in/out_station)
|
|
|
|
|
+ │ │ BLOCKED → 跳过
|
|
|
|
|
+ │ ▼ ALLOWED
|
|
|
|
|
+ │
|
|
|
|
|
+ ├─── 创建联单(lib_curl_create_bill_request) → 获取tb_num
|
|
|
|
|
+ │
|
|
|
|
|
+ ├─── 上传照片(低位+高位×2组) → 平台API
|
|
|
|
|
+ │
|
|
|
|
|
+ ├─── 飞书通知(send_feishu_msg) → 飞书开放API
|
|
|
|
|
+ │
|
|
|
|
|
+ └─── 称重+MQTT(weight_scale → mqtt_publish) → MQTT Broker
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 4. 核心模块技术方案
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 车牌识别引擎(HyperLPR3 + MNN)
|
|
|
|
|
+
|
|
|
|
|
+#### 4.1.1 初始化流程
|
|
|
|
|
+```cpp
|
|
|
|
|
+// hyperlpr_lib_init() — plate_recognizer.cpp
|
|
|
|
|
+HLPR_ContextConfiguration configuration = { 0 };
|
|
|
|
|
+configuration.models_path = (char*)model.c_str(); // hyperlpr3/resource/models/r2_mobile
|
|
|
|
|
+configuration.max_num = 2; // 每帧最多检测2个车牌
|
|
|
|
|
+configuration.det_level = DETECT_LEVEL_HIGH; // 高精度检测模式
|
|
|
|
|
+configuration.use_half = false; // 不使用FP16半精度
|
|
|
|
|
+configuration.nms_threshold = 0.5f; // NMS非极大值抑制阈值
|
|
|
|
|
+configuration.rec_confidence_threshold = 0.6f; // 识别置信度阈值
|
|
|
|
|
+configuration.box_conf_threshold = 0.30f; // 检测框置信度阈值
|
|
|
|
|
+configuration.threads = 1; // 推理线程数
|
|
|
|
|
+plate_rec_app.hlpr_ctx = HLPR_CreateContext(&configuration);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.1.2 推理流程
|
|
|
|
|
+1. `capture_do_work_thread()` 主循环每500ms执行一次
|
|
|
|
|
+2. 从4路RTSPCapture获取最新帧
|
|
|
|
|
+3. ROI裁剪(如启用)→ `HLPR_CreateDataBuffer` → `HLPR_UpdateImage`
|
|
|
|
|
+4. 遍历检测结果,按置信度更新 `CarPlateInfo`
|
|
|
|
|
+5. 置信度 ≥ `PLATE_LOG_THRESHOLD` → 保存照片到磁盘
|
|
|
|
|
+6. 置信度 ≥ `PLATE_CONFIDENCE_THRESHOLD` → 触发上传流程
|
|
|
|
|
+
|
|
|
|
|
+#### 4.1.3 ROI裁剪策略
|
|
|
|
|
+```cpp
|
|
|
|
|
+struct ROIConfig {
|
|
|
|
|
+ int x, y, width, height; // 裁剪区域
|
|
|
|
|
+ bool enabled; // 是否启用
|
|
|
|
|
+ cv::Rect getRect(int img_w, int img_h); // 安全边界裁剪
|
|
|
|
|
+};
|
|
|
|
|
+// 进站ROI: g_roi_in, 出站ROI: g_roi_out
|
|
|
|
|
+// 支持通过Web界面手动绘制调整,写回config.ini
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.1.4 车牌类型映射
|
|
|
|
|
+```cpp
|
|
|
|
|
+static const std::vector<std::string> TYPES = {
|
|
|
|
|
+ "蓝牌", "黄牌", "白牌单层", "绿牌", "黑牌港澳", "香港单层",
|
|
|
|
|
+ "香港双层", "澳门单层", "澳门双层", "黄牌双层"
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.1.5 关键结构体
|
|
|
|
|
+```cpp
|
|
|
|
|
+struct CarPlateInfo {
|
|
|
|
|
+ std::string code; // 车牌号
|
|
|
|
|
+ float confidence; // 识别置信度
|
|
|
|
|
+ std::string type; // 车牌类型(蓝牌/黄牌等)
|
|
|
|
|
+ cv::Mat image_front; // 前向照片
|
|
|
|
|
+ cv::Mat image_side; // 侧向照片
|
|
|
|
|
+ int db_id; // 数据库记录ID
|
|
|
|
|
+ bool is_inbound/outbound; // 进站/出站标记
|
|
|
|
|
+ time_t db_capture_time; // 照片保存时固定的capture_time
|
|
|
|
|
+ std::atomic<bool> upload_in_progress{false}; // P0: 上传竞态保护
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 RTSP视频采集
|
|
|
|
|
+
|
|
|
|
|
+#### 4.2.1 类设计
|
|
|
|
|
+```cpp
|
|
|
|
|
+class RTSPCapture {
|
|
|
|
|
+ std::string rtspUrl_;
|
|
|
|
|
+ DecodeMode decodeMode_; // AUTO / SOFT / DRM
|
|
|
|
|
+ std::string drmDevice_; // 默认 /dev/dri/renderD128
|
|
|
|
|
+ std::string transport_; // tcp / udp
|
|
|
|
|
+ AVFormatContext* fmtCtx_;
|
|
|
|
|
+ AVCodecContext* codecCtx_;
|
|
|
|
|
+ AVBufferRef* hwDeviceCtx_; // DRM硬件设备上下文
|
|
|
|
|
+ SwsContext* swsCtx_; // 像素格式转换上下文
|
|
|
|
|
+ std::deque<cv::Mat> frameQueue_; // 帧队列(最大10帧)
|
|
|
|
|
+ std::thread captureThread_;
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.2.2 FFmpeg解码流程
|
|
|
|
|
+1. `avformat_open_input()` — 打开RTSP流
|
|
|
|
|
+2. `avformat_find_stream_info()` — 获取流信息
|
|
|
|
|
+3. `av_find_best_stream()` — 查找视频流
|
|
|
|
|
+4. 根据codec判断:HEVC → 尝试DRM硬解,否则软解
|
|
|
|
|
+5. `avcodec_open2()` — 打开解码器(thread_count=4)
|
|
|
|
|
+6. `captureLoop()` 循环读取帧 → `decodeFrameToBGR()` → 入队
|
|
|
|
|
+
|
|
|
|
|
+#### 4.2.3 DRM硬件加速配置
|
|
|
|
|
+```cpp
|
|
|
|
|
+bool initDRMHwAccel() {
|
|
|
|
|
+ AVHWDeviceType type = av_hwdevice_find_type_by_name("drm");
|
|
|
|
|
+ av_hwdevice_ctx_create(&hwDeviceCtx_, type, "/dev/dri/renderD128", nullptr, 0);
|
|
|
|
|
+ codecCtx_->hw_device_ctx = av_buffer_ref(hwDeviceCtx_);
|
|
|
|
|
+}
|
|
|
|
|
+// 解码后检测 AV_PIX_FMT_DRM_PRIME 格式 → av_hwframe_transfer_data 转CPU
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.2.4 帧队列管理
|
|
|
|
|
+- 队列容量:最大10帧,超出时丢弃最旧帧
|
|
|
|
|
+- `getFrame()` — 取出并移除最新帧(供识别线程使用)
|
|
|
|
|
+- `peekFrame()` — 只读取不移除(供MJPEG视频流使用,避免与识别线程竞争)
|
|
|
|
|
+- 解码线程与消费线程通过 `std::mutex + condition_variable` 同步
|
|
|
|
|
+
|
|
|
|
|
+#### 4.2.5 重连策略
|
|
|
|
|
+- 读取失败(`av_read_frame` 返回错误)→ `cleanupFFmpeg()` → 等待3秒 → 重新 `initFFmpeg()`
|
|
|
|
|
+- 连接超时:5秒(`timeout=5000000`微秒)
|
|
|
|
|
+- RTSP传输:默认TCP(可配置UDP)
|
|
|
|
|
+- 低延迟优化:`fflags=nobuffer+genpts`, `flags=low_delay`
|
|
|
|
|
+
|
|
|
|
|
+#### 4.2.6 FFmpeg选项
|
|
|
|
|
+```cpp
|
|
|
|
|
+av_dict_set(&opts, "rtsp_transport", "tcp", 0);
|
|
|
|
|
+av_dict_set(&opts, "timeout", "5000000", 0); // 5秒超时
|
|
|
|
|
+av_dict_set(&opts, "fflags", "nobuffer+genpts", 0);
|
|
|
|
|
+av_dict_set(&opts, "flags", "low_delay", 0);
|
|
|
|
|
+av_dict_set(&opts, "avoid_negative_ts", "make_zero", 0);
|
|
|
|
|
+av_dict_set(&opts, "err_detect", "ignore_err", 0);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 交替锁定机制
|
|
|
|
|
+
|
|
|
|
|
+#### 4.3.1 核心规则(fix17版)
|
|
|
|
|
+| 状态转换 | 等待时间 | 超时行为 |
|
|
|
|
|
+|----------|----------|----------|
|
|
|
|
|
+| 进站完成 → 允许出站 | `in_out_interval` (默认300秒) | 等待≤2小时,超时清零 |
|
|
|
|
|
+| 出站完成 → 允许进站 | `in_out_interval + TIME_WINDOW` (默认600秒) | 等待≤2小时,超时清零 |
|
|
|
|
|
+| 出站无进站记录 | 永远拦截 | 必须先进站才能出站 |
|
|
|
|
|
+| 同方向连续操作 | 永远拦截 | 必须先完成对端操作 |
|
|
|
|
|
+
|
|
|
|
|
+#### 4.3.2 状态机
|
|
|
|
|
+```
|
|
|
|
|
+INBOUND_ALLOWED → 上次出站完成,等待 in_out_interval+TIME_WINDOW 后允许进站
|
|
|
|
|
+OUTBOUND_ALLOWED → 上次进站完成,等待 in_out_interval 后允许出站
|
|
|
|
|
+无记录出站 → 永远拦截(必须先进站)
|
|
|
|
|
+锁定超2小时 → 自动清零释放(视为车辆已离开)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.3.3 关键函数
|
|
|
|
|
+```cpp
|
|
|
|
|
+bool try_lock_in_station(const std::string& plate); // 原子锁定进站
|
|
|
|
|
+bool try_lock_out_station(const std::string&plate); // 原子锁定出站
|
|
|
|
|
+void lock_in_station(const std::string& plate); // 确认进站锁定
|
|
|
|
|
+void lock_out_station(const std::string& plate); // 确认出站锁定
|
|
|
|
|
+void unlock_rollback_station(const std::string& plate, bool is_in); // 失败回滚
|
|
|
|
|
+void cleanup_station_cache(); // 2小时超时清理
|
|
|
|
|
+void load_station_cache_from_db(); // 重启恢复
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.3.4 持久化
|
|
|
|
|
+- 数据库表:`station_lock_cache`
|
|
|
|
|
+- 字段:`plate_number`, `last_in_time`, `last_out_time`, `current_mode`
|
|
|
|
|
+- 重启后自动从数据库恢复锁定状态,超时记录直接删除
|
|
|
|
|
+
|
|
|
|
|
+### 4.4 称重调度器
|
|
|
|
|
+
|
|
|
|
|
+#### 4.4.1 耀华仪表通信协议
|
|
|
|
|
+```
|
|
|
|
|
+帧格式: 0x02 + sign(1字节) + digits(8字节) + status(1字节) + 0x03
|
|
|
|
|
+帧长度: 12字节
|
|
|
|
|
+数据解析: weight_kg = digits_value / 100.0 (单位: 分→公斤)
|
|
|
|
|
+有效状态: 排除 status='G'/'H' 的帧
|
|
|
|
|
+重量范围: g_weight_min_kg ~ g_weight_max_kg
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.4.2 TCP连接管理
|
|
|
|
|
+- 连接超时:`tcp_connect_timeout`(可配置)
|
|
|
|
|
+- 读取间隔:200ms
|
|
|
|
|
+- 断线重连:5秒延迟自动重连
|
|
|
|
|
+- 接收缓冲区:1024字节
|
|
|
|
|
+
|
|
|
|
|
+#### 4.4.3 稳定读数检测
|
|
|
|
|
+```cpp
|
|
|
|
|
+// 稳定性判定:连续 g_weight_stable_samples 个样本波动 ≤ g_weight_stable_threshold
|
|
|
|
|
+g_stability_samples.push_back(weight_kg);
|
|
|
|
|
+if (size > g_weight_stable_samples) g_stability_samples.erase(begin);
|
|
|
|
|
+// 候选数据:收集 g_weight_candidate_count 个有效读数
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.4.4 称重+MQTT异步任务队列
|
|
|
|
|
+```cpp
|
|
|
|
|
+struct WeightMqttTask {
|
|
|
|
|
+ std::string plate;
|
|
|
|
|
+ int station_type; // 1=进站, 2=出站
|
|
|
|
|
+ std::string tb_num;
|
|
|
|
|
+};
|
|
|
|
|
+// 去重机制:30秒内同车牌只发送一次MQTT消息
|
|
|
|
|
+static std::unordered_map<std::string, time_t> g_mqtt_dedup_map;
|
|
|
|
|
+static const int MQTT_DEDUP_INTERVAL_SEC = 30;
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.5 Web管理后台
|
|
|
|
|
+
|
|
|
|
|
+#### 4.5.1 框架与配置
|
|
|
|
|
+- **HTTP框架**: cpp-httplib(header-only)
|
|
|
|
|
+- **SSL支持**: OpenSSL,通过 `httplib::SSLServer` 创建HTTPS服务器
|
|
|
|
|
+- **默认端口**: 8080(从config.ini `[server] ip` 字段解析)
|
|
|
|
|
+- **HTTP重定向**: 可选HTTP→HTTPS自动跳转(fix24-v19)
|
|
|
|
|
+
|
|
|
|
|
+#### 4.5.2 SSL性能优化
|
|
|
|
|
+```cpp
|
|
|
|
|
+// SSL会话缓存:10分钟有效期
|
|
|
|
|
+SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
|
|
|
|
|
+SSL_CTX_set_timeout(ssl_ctx, 600);
|
|
|
|
|
+// 优先使用ECDHE+AESGCM/ChaCha20密码套件
|
|
|
|
|
+SSL_CTX_set_cipher_list(ssl_ctx,
|
|
|
|
|
+ "ECDHE-ECDSA-AES128-GCM-SHA256:"
|
|
|
|
|
+ "ECDHE-RSA-AES128-GCM-SHA256:"
|
|
|
|
|
+ "ECDHE-ECDSA-CHACHA20-POLY1305:...");
|
|
|
|
|
+SSL_CTX_set_ecdh_auto(ssl_ctx, 1); // 优先X25519
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.5.3 路由表
|
|
|
|
|
+
|
|
|
|
|
+| 路径 | 方法 | 功能 | 认证要求 |
|
|
|
|
|
+|------|------|------|----------|
|
|
|
|
|
+| `/` | GET | 监控首页 | 公开/基础 |
|
|
|
|
|
+| `/video` | GET | 4路视频预览 | 公开/基础 |
|
|
|
|
|
+| `/login` | GET | 登录页面 | 公开 |
|
|
|
|
|
+| `/config` | GET | 系统配置 | 管理员 |
|
|
|
|
|
+| `/locks` | GET | 锁定管理 | 公开/基础 |
|
|
|
|
|
+| `/system` | GET | 系统监控 | 公开/基础 |
|
|
|
|
|
+| `/api/monitor` | GET | 监控指标JSON | 公开 |
|
|
|
|
|
+| `/api/messages` | GET | 记录列表JSON | 公开 |
|
|
|
|
|
+| `/api/system/status` | GET | 系统资源状态 | 公开 |
|
|
|
|
|
+| `/api/frpc/status` | GET | frpc隧道状态 | 管理员 |
|
|
|
|
|
+| `/api/frpc/config` | GET/POST | frpc配置读写 | 超级管理员 |
|
|
|
|
|
+| `/api/auth/login` | POST | 登录认证 | 公开 |
|
|
|
|
|
+| `/api/auth/logout` | POST | 登出 | 已登录 |
|
|
|
|
|
+| `/api/auth/status` | GET | 会话状态 | 公开 |
|
|
|
|
|
+| `/api/station_locks` | GET | 锁定记录列表 | 公开 |
|
|
|
|
|
+| `/api/clear_lock` | POST | 清除锁定 | 管理员 |
|
|
|
|
|
+| `/api/video/{stream}` | GET | MJPEG视频流 | 公开 |
|
|
|
|
|
+| `/api/photo/{path}` | GET | 照片访问 | 公开 |
|
|
|
|
|
+
|
|
|
|
|
+#### 4.5.4 认证系统
|
|
|
|
|
+
|
|
|
|
|
+**架构**: bcrypt密码哈希 + 内存+SQLite双层Session + CSRF Token + IP速率限制
|
|
|
|
|
+
|
|
|
|
|
+```cpp
|
|
|
|
|
+// auth_db.h — 数据库层
|
|
|
|
|
+struct User {
|
|
|
|
|
+ int id; std::string username; std::string password_hash;
|
|
|
|
|
+ int role; // 0=user, 1=admin, 2=superadmin
|
|
|
|
|
+ int enabled;
|
|
|
|
|
+ int failed_attempts; time_t locked_until;
|
|
|
|
|
+};
|
|
|
|
|
+struct Session {
|
|
|
|
|
+ std::string session_id; // 64字符hex
|
|
|
|
|
+ std::string csrf_token; // 64字符hex
|
|
|
|
|
+ int user_id; std::string username;
|
|
|
|
|
+ time_t created_at, last_active, expires_at;
|
|
|
|
|
+ int remember; // 1=延长会话(7天)
|
|
|
|
|
+ int user_role;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 数据库表: users, sessions, audit_log
|
|
|
|
|
+// WAL模式 + busy_timeout=5000ms
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**Session管理**:
|
|
|
|
|
+- 空闲超时: 30分钟 (1800秒)
|
|
|
|
|
+- 绝对超时: 8小时 (28800秒)
|
|
|
|
|
+- "记住我": 7天 (604800秒)
|
|
|
|
|
+- 内存缓存 + SQLite持久化双层架构
|
|
|
|
|
+
|
|
|
|
|
+**速率限制**:
|
|
|
|
|
+- 滑动窗口: 5分钟 (300秒)
|
|
|
|
|
+- 最大尝试: 5次/IP
|
|
|
|
|
+- 超限返回 HTTP 429
|
|
|
|
|
+
|
|
|
|
|
+**CSRF保护**:
|
|
|
|
|
+- 每个Session生成64字符hex CSRF Token
|
|
|
|
|
+- 状态变更请求(POST/PUT/DELETE)必须携带 `X-CSRF-Token` 头
|
|
|
|
|
+
|
|
|
|
|
+**安全响应头**:
|
|
|
|
|
+- `X-Content-Type-Options: nosniff`
|
|
|
|
|
+- `X-Frame-Options: DENY`
|
|
|
|
|
+- `X-XSS-Protection: 1; mode=block`
|
|
|
|
|
+- `Content-Security-Policy: default-src 'self'`
|
|
|
|
|
+
|
|
|
|
|
+### 4.6 飞书/MQTT推送
|
|
|
|
|
+
|
|
|
|
|
+#### 4.6.1 飞书消息
|
|
|
|
|
+
|
|
|
|
|
+**API**: `POST https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id`
|
|
|
|
|
+
|
|
|
|
|
+**消息格式**:
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "receive_id": "{chat_id}",
|
|
|
|
|
+ "msg_type": "text",
|
|
|
|
|
+ "content": "{\"text\":\"工程名: xxx\\n车牌号: xxx\\n时间: xxx\\n...\"}"
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**重试策略**:
|
|
|
|
|
+- 最大重试: 4次(初始请求 + 3次重试)
|
|
|
|
|
+- 退让策略: 指数退让 2^n 秒 (2s, 4s, 8s)
|
|
|
|
|
+- 网络超时/连接失败 → 重试;业务错误 → 直接终止
|
|
|
|
|
+
|
|
|
|
|
+**Token管理**:
|
|
|
|
|
+- `g_tenant_token`: 飞书租户访问令牌
|
|
|
|
|
+- 过期时间: `g_token_expire_time`
|
|
|
|
|
+- 刷新缓冲: 300秒 (`TOKEN_REFRESH_BUFFER_SECONDS`)
|
|
|
|
|
+
|
|
|
|
|
+#### 4.6.2 MQTT客户端
|
|
|
|
|
+
|
|
|
|
|
+**架构**: 基于 `mosquittopp` 类,继承回调机制
|
|
|
|
|
+
|
|
|
|
|
+```cpp
|
|
|
|
|
+class MqttClient : public mosqpp::mosquittopp {
|
|
|
|
|
+ std::atomic<bool> connected{false};
|
|
|
|
|
+ std::atomic<MqttState> current_state{DISCONNECTED};
|
|
|
|
|
+ static const int MAX_RECONNECT_ATTEMPTS = 30;
|
|
|
|
|
+};
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**连接管理**:
|
|
|
|
|
+- 异步连接: `connect_async(host, port, keepalive=60)`
|
|
|
|
|
+- 状态机: DISCONNECTED → CONNECTING → CONNECTED → STABLE (5秒后)
|
|
|
|
|
+- 最大重连: 30次
|
|
|
|
|
+- ClientID格式: `{client_id}_{pid}_{timestamp}`
|
|
|
|
|
+
|
|
|
|
|
+**消息发布**:
|
|
|
|
|
+- QoS: 1 (至少一次)
|
|
|
|
|
+- 发布后等待ACK确认 (`acked` 原子变量)
|
|
|
|
|
+- 连接断开后自动重连
|
|
|
|
|
+
|
|
|
|
|
+**消息内容**: 车牌号 + 车牌颜色 + 车型 + 联单编号 + 进出站类型
|
|
|
|
|
+
|
|
|
|
|
+### 4.7 数据库操作
|
|
|
|
|
+
|
|
|
|
|
+#### 4.7.1 SQLite表结构
|
|
|
|
|
+
|
|
|
|
|
+**主表: `upload_records`**
|
|
|
|
|
+```sql
|
|
|
|
|
+CREATE TABLE upload_records (
|
|
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
+ create_time DATETIME DEFAULT (datetime('now', 'localtime')),
|
|
|
|
|
+ capture_time INTEGER, -- fix24-v13: 抓拍时刻Unix时间戳
|
|
|
|
|
+ plate_number TEXT NOT NULL,
|
|
|
|
|
+ tb_num TEXT NOT NULL,
|
|
|
|
|
+ station_type INTEGER, -- 1=进站, 2=出站
|
|
|
|
|
+ lo_photo_path TEXT,
|
|
|
|
|
+ hi_photo_path TEXT,
|
|
|
|
|
+ lo_upload_status INTEGER DEFAULT 0,
|
|
|
|
|
+ hi_upload_status INTEGER DEFAULT 0,
|
|
|
|
|
+ feishu_status INTEGER DEFAULT 0,
|
|
|
|
|
+ retry_count INTEGER DEFAULT 0
|
|
|
|
|
+);
|
|
|
|
|
+-- 索引
|
|
|
|
|
+CREATE INDEX idx_plate_number ON upload_records(plate_number);
|
|
|
|
|
+CREATE INDEX idx_tb_num ON upload_records(tb_num);
|
|
|
|
|
+CREATE INDEX idx_create_time ON upload_records(create_time);
|
|
|
|
|
+CREATE INDEX idx_plate_station_time_status ON upload_records(
|
|
|
|
|
+ plate_number, station_type, create_time, lo_upload_status, hi_upload_status);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**交替锁定表: `station_lock_cache`**
|
|
|
|
|
+```sql
|
|
|
|
|
+CREATE TABLE station_lock_cache (
|
|
|
|
|
+ plate_number TEXT PRIMARY KEY,
|
|
|
|
|
+ last_in_time INTEGER,
|
|
|
|
|
+ last_out_time INTEGER,
|
|
|
|
|
+ current_mode INTEGER -- 0=INBOUND_ALLOWED, 1=OUTBOUND_ALLOWED
|
|
|
|
|
+);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**TIME_WINDOW缓存表: `time_window_cache`**
|
|
|
|
|
+```sql
|
|
|
|
|
+-- 存储进站/出站工单配额缓存
|
|
|
|
|
+CREATE TABLE time_window_cache (...);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**认证数据库表** (独立SQLite文件):
|
|
|
|
|
+```sql
|
|
|
|
|
+CREATE TABLE users (
|
|
|
|
|
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
+ username TEXT UNIQUE NOT NULL,
|
|
|
|
|
+ password_hash TEXT NOT NULL, -- bcrypt hash
|
|
|
|
|
+ role INTEGER DEFAULT 0,
|
|
|
|
|
+ enabled INTEGER DEFAULT 1,
|
|
|
|
|
+ failed_attempts INTEGER DEFAULT 0,
|
|
|
|
|
+ locked_until INTEGER DEFAULT 0,
|
|
|
|
|
+ created_at INTEGER NOT NULL,
|
|
|
|
|
+ updated_at INTEGER NOT NULL
|
|
|
|
|
+);
|
|
|
|
|
+CREATE TABLE sessions (...);
|
|
|
|
|
+CREATE TABLE audit_log (...);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**系统监控数据库** (独立SQLite文件):
|
|
|
|
|
+- 路径: `data/system_metrics.db`
|
|
|
|
|
+- 用途: CPU/内存/温度/磁盘历史数据
|
|
|
|
|
+
|
|
|
|
|
+#### 4.7.2 数据库配置
|
|
|
|
|
+```cpp
|
|
|
|
|
+sqlite3_busy_timeout(g_db, 100); // busy超时100ms
|
|
|
|
|
+SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX
|
|
|
|
|
+// WAL模式(认证数据库)
|
|
|
|
|
+// 认证数据库: busy_timeout=5000ms
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.7.3 缓存策略
|
|
|
|
|
+- TIME_WINDOW缓存: 内存 + SQLite 双层
|
|
|
|
|
+- 交替锁定缓存: 内存(`g_plate_station_cache`) + SQLite 双层
|
|
|
|
|
+- 联单缓存: `g_in_bill_cache` / `g_out_bill_cache` (内存)
|
|
|
|
|
+- 数据库自动清理: 每日23:10执行 `db_cleanup_old_records` + `db_vacuum`
|
|
|
|
|
+- 数据库健康检查: 每60秒 `SELECT 1` 探活,失败自动重连
|
|
|
|
|
+- DB容量阈值: 500MB触发清理 (`DB_CLEANUP_THRESHOLD_MB`)
|
|
|
|
|
+
|
|
|
|
|
+### 4.8 日志管理
|
|
|
|
|
+
|
|
|
|
|
+#### 4.8.1 内存日志缓冲架构(fix24-v36)
|
|
|
|
|
+```
|
|
|
|
|
+stdout/stderr → pipe() → 读取线程 → 内存buffer(vector<string>)
|
|
|
|
|
+ │
|
|
|
|
|
+ ├── Web API读取(零磁盘I/O)
|
|
|
|
|
+ └── 定时刷盘(默认23:20) → 磁盘文件
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.8.2 关键配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `enabled` | true | 1=内存缓冲模式, 0=直写文件 |
|
|
|
|
|
+| `buffer_size_mb` | 20MB | 内存缓冲区上限 |
|
|
|
|
|
+| `flush_time` | 23:20 | 每日刷盘时间 |
|
|
|
|
|
+| `retention_days` | 30天 | 日志保留天数 |
|
|
|
|
|
+| `redirect_stdout` | true | 是否重定向stdout/stderr |
|
|
|
|
|
+| `auto_flush_on_exit` | true | 程序退出时自动刷盘 |
|
|
|
|
|
+
|
|
|
|
|
+#### 4.8.3 刷盘策略
|
|
|
|
|
+- 定时刷盘: 到达 `flush_time` 时将内存buffer全量写入磁盘
|
|
|
|
|
+- 退出刷盘: 程序优雅退出时执行最后一次刷盘
|
|
|
|
|
+- 容量控制: 超过 `buffer_size_mb` 时丢弃最旧日志
|
|
|
|
|
+- 文件清理: 凌晨2:00按 `retention_days` 清理过期日志文件
|
|
|
|
|
+
|
|
|
|
|
+### 4.9 frpc内网穿透管理
|
|
|
|
|
+
|
|
|
|
|
+#### 4.9.1 状态获取
|
|
|
|
|
+通过 frpc admin HTTP API 获取隧道状态:
|
|
|
|
|
+```cpp
|
|
|
|
|
+std::string call_admin_api(const std::string &endpoint) {
|
|
|
|
|
+ // URL: http://{admin_addr}:{admin_port}/api/serverinfo
|
|
|
|
|
+ // Basic Auth: g_frpc_admin_user : g_frpc_admin_password
|
|
|
|
|
+ // 超时: connect=1s, total=2s
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.9.2 进程检测(三重验证)
|
|
|
|
|
+1. `systemctl is-active frpc` — systemd状态
|
|
|
|
|
+2. `pgrep -x frpc` — 进程存在性
|
|
|
|
|
+3. frpc admin API可达性
|
|
|
|
|
+
|
|
|
|
|
+#### 4.9.3 配置热更新
|
|
|
|
|
+- 读取: 直接读取 `frpc.toml` 文件内容
|
|
|
|
|
+- 保存: 自动备份旧文件到 `backup_path` → 写入新配置
|
|
|
|
|
+- 重启: `systemctl restart frpc` → 等待 `restart_check_timeout` 秒
|
|
|
|
|
+- 自动回滚: 重启后检测进程存活,失败则恢复备份配置
|
|
|
|
|
+
|
|
|
|
|
+#### 4.9.4 安全限制
|
|
|
|
|
+- **admin API仅允许本机127.0.0.1调用**
|
|
|
|
|
+- 禁止通过frp隧道暴露到公网
|
|
|
|
|
+- 受保护隧道列表: `g_frpc_protected_tunnels`(不可删除)
|
|
|
|
|
+
|
|
|
|
|
+### 4.10 SSH密钥管理
|
|
|
|
|
+
|
|
|
|
|
+#### 4.10.1 功能
|
|
|
|
|
+- 读取/添加/删除 `authorized_keys` 文件中的SSH公钥
|
|
|
|
|
+- 支持密钥类型验证: ssh-rsa, ssh-dss, ssh-ed25519, ecdsa-sha2-*
|
|
|
|
|
+
|
|
|
|
|
+#### 4.10.2 密钥格式验证
|
|
|
|
|
+```cpp
|
|
|
|
|
+bool validate_key_format(const std::string &key_line) {
|
|
|
|
|
+ // 格式: type base64_data [comment]
|
|
|
|
|
+ // type必须是已知SSH密钥类型
|
|
|
|
|
+ // base64数据至少20字符
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4.10.3 配置
|
|
|
|
|
+- 密钥文件路径: `g_ssh_keys_path` (默认 `~/.ssh/authorized_keys`)
|
|
|
|
|
+- 通过Web管理界面操作
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 5. 配置文件详解
|
|
|
|
|
+
|
|
|
|
|
+### 5.1 config.ini 完整配置段
|
|
|
|
|
+
|
|
|
|
|
+#### [config] — 核心业务配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `project_name` | (必填) | 工程名 |
|
|
|
|
|
+| `point_number` | (必填) | 点位编号 |
|
|
|
|
|
+| `throughway` | (必填) | 出入口编号 |
|
|
|
|
|
+| `rtsp_url_front_in` | (必填) | 进站前向摄像头RTSP URL |
|
|
|
|
|
+| `rtsp_url_front_out` | (必填) | 出站前向摄像头RTSP URL |
|
|
|
|
|
+| `rtsp_url_side_in` | (必填) | 进站侧向摄像头RTSP URL |
|
|
|
|
|
+| `rtsp_url_side_out` | (必填) | 出站侧向摄像头RTSP URL |
|
|
|
|
|
+| `app_api` | (必填) | 平台API地址 |
|
|
|
|
|
+| `app_key` | (必填) | 平台API Key |
|
|
|
|
|
+| `app_secret` | (必填) | 平台API Secret |
|
|
|
|
|
+| `DEBUG_LOG` | 0 | 调试日志(1=开启, 0=正式) |
|
|
|
|
|
+| `TestFlag` | 0 | 测试环境标记 |
|
|
|
|
|
+| `wType` | 1 | 工地类型(1=工地, 2=码头, 3=回填) |
|
|
|
|
|
+| `TIME_WINDOW` | 10 | 时间窗口(分钟),控制进站/出站配额 |
|
|
|
|
|
+| `MAX_PHOTO_GROUPS` | 2 | 每侧最大照片组数 |
|
|
|
|
|
+| `in_out_interval` | 5 | 进出站间隔(分钟),转换为秒: ×60 |
|
|
|
|
|
+| `PLATE_CONFIDENCE_THRESHOLD` | 0.8 | 上传触发置信度阈值 |
|
|
|
|
|
+| `PLATE_LOG_THRESHOLD` | 0.5 | 日志记录置信度阈值 |
|
|
|
|
|
+| `AlternatingMerge` | 0 | 交替锁定开关(1=启用) |
|
|
|
|
|
+| `roi_in_x/y/w/h` | 0 | 进站ROI区域坐标 |
|
|
|
|
|
+| `roi_in_enabled` | 0 | 进站ROI启用(1=启用) |
|
|
|
|
|
+| `roi_out_x/y/w/h` | 0 | 出站ROI区域坐标 |
|
|
|
|
|
+| `roi_out_enabled` | 0 | 出站ROI启用 |
|
|
|
|
|
+| `roi_debug_enabled` | 0 | ROI调试模式 |
|
|
|
|
|
+| `hw_decode_mode` | auto | 解码模式(auto/soft/drm) |
|
|
|
|
|
+| `hw_decode_device` | /dev/dri/renderD128 | DRM设备路径 |
|
|
|
|
|
+| `rtsp_transport` | tcp | RTSP传输协议(tcp/udp) |
|
|
|
|
|
+| `PhotoMaxCapacityMB` | 1024 | 照片存储容量阈值(MB) |
|
|
|
|
|
+| `LogRetentionDays` | 30 | 日志保留天数 |
|
|
|
|
|
+
|
|
|
|
|
+#### [server] — Web服务配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `ip` | http://0.0.0.0:8080 | 服务地址(端口从此字段解析) |
|
|
|
|
|
+
|
|
|
|
|
+#### [ssl] — HTTPS/SSL配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `enabled` | 0 | SSL启用(1=启用) |
|
|
|
|
|
+| `cert_path` | (空) | SSL证书文件路径 |
|
|
|
|
|
+| `key_path` | (空) | SSL私钥文件路径 |
|
|
|
|
|
+
|
|
|
|
|
+#### [auth] — 认证系统配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `enabled` | 0 | 认证启用(1=启用) |
|
|
|
|
|
+| `db_path` | data/auth.db | 认证数据库路径(相对pathComm) |
|
|
|
|
|
+
|
|
|
|
|
+#### [feishu] — 飞书通知配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `APP_ID` | (必填) | 飞书应用ID |
|
|
|
|
|
+| `APP_SECRET` | (必填) | 飞书应用密钥 |
|
|
|
|
|
+| `CHAT_ID` | (必填) | 飞书群聊ID |
|
|
|
|
|
+| `TIMEOUT_SECOND` | 10 | HTTP超时(秒) |
|
|
|
|
|
+| `WarningSigns` | 0 | 抑制紧急告警(1=抑制) |
|
|
|
|
|
+
|
|
|
|
|
+#### [weight] — 称重系统配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `flagWeight` | 0 | 称重启用(1=启用) |
|
|
|
|
|
+| `weight_threshold_in` | (必填) | 进站重量阈值(公斤) |
|
|
|
|
|
+| `weight_threshold_out` | (必填) | 出站重量阈值(公斤) |
|
|
|
|
|
+| `default_entry_weight` | 0 | 默认进站重量 |
|
|
|
|
|
+| `default_exit_weight` | 0 | 默认出站重量 |
|
|
|
|
|
+| `weight_detection_time` | 10 | 称重检测时间(秒) |
|
|
|
|
|
+| `tcp_connect_timeout` | 5 | TCP连接超时(秒) |
|
|
|
|
|
+| `weight_server_ip` | (必填) | 仪表IP地址 |
|
|
|
|
|
+| `weight_server_port` | (必填) | 仪表端口 |
|
|
|
|
|
+| `MAX_WEIGHT` | 100000 | 最大有效重量(公斤) |
|
|
|
|
|
+| `STABLE_SAMPLE_COUNT` | 5 | 稳定判定样本数 |
|
|
|
|
|
+| `STABLE_THRESHOLD_KG` | 50 | 稳定波动阈值(公斤) |
|
|
|
|
|
+| `CANDIDATE_DATA_COUNT` | 10 | 候选数据数量 |
|
|
|
|
|
+| `MAX_UPLOAD_RETRIES` | 3 | 最大重试次数 |
|
|
|
|
|
+| `RETRY_DELAY_MS` | 1000 | 重试延迟(毫秒) |
|
|
|
|
|
+
|
|
|
|
|
+#### [MQTT] — MQTT配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `MQTT_HOST` | (空) | MQTT Broker地址 |
|
|
|
|
|
+| `MQTT_PORT` | 1883 | MQTT端口 |
|
|
|
|
|
+| `MQTT_USER` | (空) | 用户名 |
|
|
|
|
|
+| `MQTT_PASS` | (空) | 密码 |
|
|
|
|
|
+| `MQTT_TOPIC` | (必填) | 发布主题 |
|
|
|
|
|
+| `MQTT_CLIENT_ID` | PlateRecApp | 客户端ID |
|
|
|
|
|
+| `PLATE_COLOR` | 黄色 | 默认车牌颜色 |
|
|
|
|
|
+| `VEHICLE_TYPE` | 渣土车 | 默认车型 |
|
|
|
|
|
+
|
|
|
|
|
+#### [system] — 系统监控配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `monitor_interval` | 30 | 采样间隔(秒) |
|
|
|
|
|
+| `temp_alert_threshold` | 80 | 温度告警阈值(°C) |
|
|
|
|
|
+| `cpu_alert_threshold` | 90 | CPU告警阈值(%) |
|
|
|
|
|
+| `db_path` | data/system_metrics.db | 监控数据库路径 |
|
|
|
|
|
+| `history_retention_days` | 30 | 全量数据保留天数 |
|
|
|
|
|
+| `aggregation_retention_days` | 365 | 聚合数据保留天数 |
|
|
|
|
|
+| `flush_time` | 23:20 | 每日刷盘时间 |
|
|
|
|
|
+
|
|
|
|
|
+#### [frpc] — frpc管理配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `config_path` | /etc/frp/frpc.toml | frpc配置文件路径 |
|
|
|
|
|
+| `backup_path` | (自动) | 备份文件路径 |
|
|
|
|
|
+| `protected_tunnels` | (空) | 受保护隧道列表(逗号分隔) |
|
|
|
|
|
+| `restart_check_timeout` | 10 | 重启后等待检测时间(秒) |
|
|
|
|
|
+| `auto_rollback` | 1 | 失败自动回滚(1=启用) |
|
|
|
|
|
+| `web_enabled` | 1 | Web管理页面开关 |
|
|
|
|
|
+| `admin_addr` | 127.0.0.1 | frpc admin API地址 |
|
|
|
|
|
+| `admin_port` | 7400 | frpc admin API端口 |
|
|
|
|
|
+| `admin_user` | (空) | admin用户名 |
|
|
|
|
|
+| `admin_password` | (空) | admin密码 |
|
|
|
|
|
+
|
|
|
|
|
+#### [terminal] — 终端/SSH配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `ttyd_port` | 7681 | ttyd Web终端端口 |
|
|
|
|
|
+| `ttyd_credential` | (空) | ttyd认证凭证(user:pass) |
|
|
|
|
|
+| `ttyd_max_clients` | 5 | 最大客户端数 |
|
|
|
|
|
+| `ssh_keys_path` | ~/.ssh/authorized_keys | SSH公钥文件路径 |
|
|
|
|
|
+
|
|
|
|
|
+#### [log] — 日志管理配置
|
|
|
|
|
+| 参数 | 默认值 | 说明 |
|
|
|
|
|
+|------|--------|------|
|
|
|
|
|
+| `enabled` | 1 | 内存日志模式(1=内存, 0=直写) |
|
|
|
|
|
+| `log_file` | PlateRecApp.log | 日志文件名 |
|
|
|
|
|
+| `buffer_size_mb` | 20 | 缓冲区大小(MB) |
|
|
|
|
|
+| `flush_time` | 23:20 | 刷盘时间(HH:MM) |
|
|
|
|
|
+| `retention_days` | 30 | 保留天数 |
|
|
|
|
|
+| `redirect_stdout` | 1 | 重定向stdout/stderr |
|
|
|
|
|
+| `auto_flush_on_exit` | 1 | 退出时自动刷盘 |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 6. 构建系统
|
|
|
|
|
+
|
|
|
|
|
+### 6.1 CMakeLists.txt 详细解析
|
|
|
|
|
+
|
|
|
|
|
+```cmake
|
|
|
|
|
+cmake_minimum_required(VERSION 3.25.1)
|
|
|
|
|
+project(HyperLPR3-LinuxAPP VERSION 1.0.0 LANGUAGES CXX C)
|
|
|
|
|
+set(CMAKE_CXX_STANDARD 17)
|
|
|
|
|
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
|
|
+set(CMAKE_CXX_EXTENSIONS OFF)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**构建类型默认**: Release
|
|
|
|
|
+
|
|
|
|
|
+### 6.2 编译优化标志
|
|
|
|
|
+
|
|
|
|
|
+| 标志 | 作用 |
|
|
|
|
|
+|------|------|
|
|
|
|
|
+| `-O3` | 最高级别速度优化 |
|
|
|
|
|
+| `-s` | 去除符号表(strip) |
|
|
|
|
|
+| `--as-needed` | 只链接实际使用的符号 |
|
|
|
|
|
+| `--gc-sections` | 删除未使用的段 |
|
|
|
|
|
+| POST_BUILD strip | 编译后strip进一步减小体积 |
|
|
|
|
|
+
|
|
|
|
|
+**可选优化**(注释状态):
|
|
|
|
|
+- `-Os`: 体积优先优化
|
|
|
|
|
+- `-flto`: 链接时优化(减小10-30%体积)
|
|
|
|
|
+- `-fvisibility=hidden`: 隐藏符号
|
|
|
|
|
+
|
|
|
|
|
+### 6.3 依赖库查找策略
|
|
|
|
|
+1. **pkg-config优先**: FreeType2, SQLite3, FFmpeg各模块
|
|
|
|
|
+2. **CMake find_package**: OpenCV, CURL, OpenSSL
|
|
|
|
|
+3. **手动查找回退**: cJSON(先pkg-config,再find_library,最后硬编码路径)
|
|
|
|
|
+4. **动态链接优先**: `BUILD_SHARED_LIBS ON`,可通过 `-DUSE_STATIC_LIBS=ON` 切换静态
|
|
|
|
|
+
|
|
|
|
|
+### 6.4 源码文件列表
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+src/
|
|
|
|
|
+├── main.cpp # 主入口 + 调度
|
|
|
|
|
+├── common.cpp/h # 全局类型定义
|
|
|
|
|
+├── config.cpp/h # 配置解析
|
|
|
|
|
+├── plate_recognizer.cpp/h # 车牌识别引擎
|
|
|
|
|
+├── rtsp_capture.cpp/h # RTSP视频采集
|
|
|
|
|
+├── web_server.cpp/h # Web管理后台
|
|
|
|
|
+├── database.cpp/h # SQLite数据库
|
|
|
|
|
+├── network_client.cpp/h # HTTP API通信
|
|
|
|
|
+├── feishu_client.cpp/h # 飞书推送
|
|
|
|
|
+├── mqtt_client.cpp/h # MQTT客户端
|
|
|
|
|
+├── weight_scale.cpp/h # 称重系统
|
|
|
|
|
+├── station_lock.cpp/h # 交替锁定
|
|
|
|
|
+├── log_manager.cpp/h # 日志管理
|
|
|
|
|
+├── frpc_manager.cpp/h # frpc管理
|
|
|
|
|
+├── ssh_manager.cpp/h # SSH管理
|
|
|
|
|
+├── system_monitor.cpp/h # 系统监控
|
|
|
|
|
+├── system_metrics_manager.cpp/h # 监控历史数据
|
|
|
|
|
+├── monitor.cpp/h # 指标打印
|
|
|
|
|
+├── utils.cpp/h # 工具函数
|
|
|
|
|
+├── auth_api.cpp/h # 认证API
|
|
|
|
|
+├── auth_crypto.cpp/h # bcrypt + 安全随机
|
|
|
|
|
+├── auth_db.cpp/h # 认证数据库
|
|
|
|
|
+├── auth_session.cpp/h # 会话管理
|
|
|
|
|
+├── auth_middleware.cpp/h # 认证中间件
|
|
|
|
|
+├── auth_rate_limiter.h # 速率限制(header-only)
|
|
|
|
|
+└── bcrypt/ # bcrypt实现
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 7. 部署方案
|
|
|
|
|
+
|
|
|
|
|
+### 7.1 目录结构
|
|
|
|
|
+```
|
|
|
|
|
+/opt/openAI/project/003.PlateRecAPP/ # pathComm (可执行文件所在目录)
|
|
|
|
|
+├── PlateRecApp # 可执行文件
|
|
|
|
|
+├── config.ini # 主配置文件
|
|
|
|
|
+├── config.txt # MD5校验配置
|
|
|
|
|
+├── simsun.ttc # 中文字体文件
|
|
|
|
|
+├── hyperlpr3/
|
|
|
|
|
+│ └── resource/models/r2_mobile/ # HyperLPR3模型文件
|
|
|
|
|
+├── assets/web/ # Web前端HTML页面
|
|
|
|
|
+│ ├── login.html
|
|
|
|
|
+│ ├── system.html
|
|
|
|
|
+│ └── ...
|
|
|
|
|
+├── data/ # 数据库目录
|
|
|
|
|
+│ ├── upload_records.db # 主数据库
|
|
|
|
|
+│ ├── auth.db # 认证数据库
|
|
|
|
|
+│ └── system_metrics.db # 监控数据库
|
|
|
|
|
+├── PlateJPG/ # 照片存储目录
|
|
|
|
|
+├── third_party/ # 第三方依赖
|
|
|
|
|
+└── sync_to_gogs.sh # Gogs同步脚本
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 7.2 systemd service 配置
|
|
|
|
|
+```ini
|
|
|
|
|
+[Unit]
|
|
|
|
|
+Description=PlateRecApp License Plate Recognition
|
|
|
|
|
+After=network.target
|
|
|
|
|
+
|
|
|
|
|
+[Service]
|
|
|
|
|
+Type=simple
|
|
|
|
|
+WorkingDirectory=/opt/openAI/project/003.PlateRecAPP
|
|
|
|
|
+ExecStart=/opt/openAI/project/003.PlateRecAPP/PlateRecApp config.ini
|
|
|
|
|
+Restart=always
|
|
|
|
|
+RestartSec=5
|
|
|
|
|
+StandardOutput=journal
|
|
|
|
|
+StandardError=journal
|
|
|
|
|
+
|
|
|
|
|
+[Install]
|
|
|
|
|
+WantedBy=multi-user.target
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 7.3 文件权限
|
|
|
|
|
+- 可执行文件: `755`
|
|
|
|
|
+- 配置文件: `644`(含敏感信息,不可公开)
|
|
|
|
|
+- 数据库目录: `755`
|
|
|
|
|
+- 照片目录: `755`
|
|
|
|
|
+
|
|
|
|
|
+### 7.4 启动流程
|
|
|
|
|
+```
|
|
|
|
|
+1. 解析可执行文件路径 → 确定 pathComm
|
|
|
|
|
+2. 读取CPU序列号 → MD5三重校验 → 验证config.txt
|
|
|
|
|
+3. 解析config.ini → 加载所有配置
|
|
|
|
|
+4. 计算时区偏移 → 初始化cURL
|
|
|
|
|
+5. 初始化SQLite数据库 → 加载缓存
|
|
|
|
|
+6. 初始化HyperLPR3模型
|
|
|
|
|
+7. 创建4路RTSPCapture → 启动采集线程
|
|
|
|
|
+8. 启动识别工作线程
|
|
|
|
|
+9. 初始化认证系统 → 启动Web服务器
|
|
|
|
|
+10. 启动称重系统 → 启动MQTT客户端
|
|
|
|
|
+11. 进入主循环(100ms间隔)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 8. 安全策略
|
|
|
|
|
+
|
|
|
|
|
+### 8.1 认证机制
|
|
|
|
|
+- **bcrypt密码哈希**: 使用系统libcrypt的`crypt_r`,成本因子自动调整
|
|
|
|
|
+- **Session管理**: 64字符hex随机session_id,内存+SQLite双层
|
|
|
|
|
+- **CSRF防护**: 每个Session独立CSRF Token,状态变更请求必须验证
|
|
|
|
|
+- **IP速率限制**: 滑动窗口5分钟/5次,超限返回429
|
|
|
|
|
+- **角色权限**: user(0) / admin(1) / superadmin(2),不同页面/API按角色控制
|
|
|
|
|
+- **账户锁定**: 连续失败登录自动锁定(`failed_attempts` + `locked_until`)
|
|
|
|
|
+- **审计日志**: 所有登录/登出/失败/超时事件记录到 `audit_log` 表
|
|
|
|
|
+
|
|
|
|
|
+### 8.2 密码存储
|
|
|
|
|
+- 算法: bcrypt(通过 `crypt_r` 系统调用)
|
|
|
|
|
+- 自动salt生成,每次哈希结果不同
|
|
|
|
|
+- 验证使用 `secure_compare` 时间安全比较(防时序攻击)
|
|
|
|
|
+
|
|
|
|
|
+### 8.3 HTTPS/SSL
|
|
|
|
|
+- 可选启用(config.ini `[ssl] enabled=1`)
|
|
|
|
|
+- 证书+私钥 PEM格式
|
|
|
|
|
+- SSL会话缓存(10分钟)
|
|
|
|
|
+- 优先ECDHE+AESGCM/ChaCha20密码套件
|
|
|
|
|
+- HTTP→HTTPS自动重定向(可选)
|
|
|
|
|
+
|
|
|
|
|
+### 8.4 frpc admin API 限制
|
|
|
|
|
+- **仅允许本机127.0.0.1调用**
|
|
|
|
|
+- 禁止通过frp隧道暴露到公网
|
|
|
|
|
+- Basic Auth认证保护
|
|
|
|
|
+- 超时严格限制(connect=1s, total=2s)
|
|
|
|
|
+
|
|
|
|
|
+### 8.5 路径安全
|
|
|
|
|
+- Web文件访问: `is_safe_file_path()` 防路径穿越
|
|
|
|
|
+- 禁止 `..` / `/` / `\` 字符
|
|
|
|
|
+- `realpath()` 验证绝对路径在基础目录内
|
|
|
|
|
+
|
|
|
|
|
+### 8.6 配置文件校验
|
|
|
|
|
+- CPU序列号 + MD5三重校验 → 防止未授权设备运行
|
|
|
|
|
+- config.ini 含敏感信息,.gitignore 已排除
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 9. 版本变更记录
|
|
|
|
|
+
|
|
|
|
|
+详见 [CHANGELOG.md](CHANGELOG.md)
|
|
|
|
|
+
|
|
|
|
|
+主要版本里程碑:
|
|
|
|
|
+- **v43**: RTSP ROI裁剪、DRM硬件解码、4路独立视频流
|
|
|
|
|
+- **v43.2 fix11-fix17**: 交替锁定机制完善(非对称等待、2小时超时清零)
|
|
|
|
|
+- **fix18**: 称重+MQTT主进程合并、日志logrotate
|
|
|
|
|
+- **fix19**: 文件清理重构(照片按容量、日志按天数)
|
|
|
|
|
+- **fix20**: 推理耗时追踪、MQTT完整迁移
|
|
|
|
|
+- **fix21-fix23**: P0时间对齐bug修复、抓拍时间全链路一致
|
|
|
|
|
+- **fix24**: 登录认证系统、Web管理功能(系统监控+frpc+SSH+日志)、HTTPS/SSL
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 10. 待改进项
|
|
|
|
|
+
|
|
|
|
|
+### 10.1 技术债务
|
|
|
|
|
+1. **配置热更新不完整**: ROI可热写回config.ini,但其他配置修改后需重启
|
|
|
|
|
+2. **认证系统JSON解析**: 使用手写简单解析而非cJSON,鲁棒性待提升
|
|
|
|
|
+3. **全局变量过多**: 大量 `extern` 全局变量,耦合度高,应逐步迁移到单例/依赖注入
|
|
|
|
|
+4. **Web前端内嵌**: HTML直接写在C++字符串中,维护困难,应迁移到独立模板
|
|
|
|
|
+5. **缺少单元测试**: 无自动化测试框架覆盖核心逻辑
|
|
|
|
|
+6. **数据库迁移**: 旧数据库需手动cp迁移,缺少自动迁移工具
|
|
|
|
|
+7. **日志内存缓冲**: 超过buffer_size时丢弃旧日志,可能丢失关键信息
|
|
|
|
|
+
|
|
|
|
|
+### 10.2 改进方向
|
|
|
|
|
+1. **gRPC替代HTTP**: 平台API通信改用gRPC提升效率
|
|
|
|
|
+2. **TensorRT替代MNN**: 利用NPU/GPU进一步加速推理
|
|
|
|
|
+3. **WebSocket替代轮询**: Web监控数据改为实时推送
|
|
|
|
|
+4. **Docker化部署**: 简化依赖安装和环境配置
|
|
|
|
|
+5. **OTA远程更新**: 结合frpc实现远程固件升级
|
|
|
|
|
+6. **多站点支持**: 单进程管理多个出入口点位
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+> **本文档持续更新**
|
|
|
|
|
+> 最后更新: 2026-07-06 | 基于 PlateRecApp v43.2 fix24 源码
|