TECH_DOC.md 37 KB

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 初始化流程

// 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裁剪策略

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 车牌类型映射

static const std::vector<std::string> TYPES = {
    "蓝牌", "黄牌", "白牌单层", "绿牌", "黑牌港澳", "香港单层",
    "香港双层", "澳门单层", "澳门双层", "黄牌双层"
};

4.1.5 关键结构体

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 类设计

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硬件加速配置

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选项

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 关键函数

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 稳定读数检测

// 稳定性判定:连续 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异步任务队列

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性能优化

// 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速率限制

// 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

消息格式:

{
    "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 类,继承回调机制

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

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

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

-- 存储进站/出站工单配额缓存
CREATE TABLE time_window_cache (...);

认证数据库表 (独立SQLite文件):

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 数据库配置

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 获取隧道状态:

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 密钥格式验证

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_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 配置

[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

主要版本里程碑:

  • 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 源码