help20200706.html 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1">
  6. <title>帮助手册 - 车牌识别系统</title>
  7. <style>
  8. *{box-sizing:border-box;margin:0;padding:0}
  9. body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f0f2f5;color:#333}
  10. .navbar{background:#1a1a2e;color:#fff;padding:12px 20px;display:flex;align-items:center;gap:20px;box-shadow:0 2px 8px rgba(0,0,0,0.15)}
  11. .navbar a{color:#ccc;text-decoration:none;padding:6px 12px;border-radius:4px;font-size:14px}
  12. .navbar a:hover,.navbar a.active{background:rgba(255,255,255,0.15);color:#fff}
  13. .navbar .brand{font-size:16px;font-weight:bold;color:#4fc3f7;margin-right:10px}
  14. .container{max-width:960px;margin:20px auto;padding:0 16px}
  15. .card{background:#fff;border-radius:8px;margin-bottom:16px;box-shadow:0 1px 4px rgba(0,0,0,0.08);overflow:hidden}
  16. .card-title{background:#263238;color:#fff;padding:10px 16px;font-size:14px;font-weight:600;display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none}
  17. .card-title:hover{background:#37474f}
  18. .card-title .arrow{transition:transform 0.2s;font-size:12px}
  19. .card-title.collapsed .arrow{transform:rotate(-90deg)}
  20. .card-body{padding:16px 20px;line-height:1.8;font-size:14px}
  21. .card-body.hidden{display:none}
  22. h3{color:#1a1a2e;margin:16px 0 8px;font-size:15px;border-left:3px solid #667eea;padding-left:10px}
  23. h4{color:#333;margin:12px 0 6px;font-size:14px}
  24. table{width:100%;border-collapse:collapse;margin:8px 0 16px;font-size:13px}
  25. th{background:#f5f5f5;padding:8px 10px;text-align:left;font-weight:600;border-bottom:2px solid #e0e0e0;white-space:nowrap}
  26. td{padding:8px 10px;border-bottom:1px solid #f0f0f0;vertical-align:top}
  27. td:first-child{font-family:monospace;color:#d32f2f;white-space:nowrap;font-size:12px}
  28. code{background:#f5f5f5;padding:2px 6px;border-radius:3px;font-size:12px;color:#d32f2f;font-family:monospace}
  29. pre{background:#1e1e2e;color:#cdd6f4;padding:14px 18px;border-radius:6px;font-size:12px;overflow-x:auto;margin:8px 0 16px;line-height:1.6}
  30. pre code{background:none;color:#cdd6f4;padding:0;font-size:12px}
  31. .tip{background:#e8f5e9;border-left:4px solid #4caf50;padding:10px 14px;margin:8px 0;border-radius:0 4px 4px 0;font-size:13px}
  32. .warn{background:#fff3e0;border-left:4px solid #ff9800;padding:10px 14px;margin:8px 0;border-radius:0 4px 4px 0;font-size:13px}
  33. .note{background:#e3f2fd;border-left:4px solid #2196f3;padding:10px 14px;margin:8px 0;border-radius:0 4px 4px 0;font-size:13px}
  34. ul{padding-left:20px;margin:6px 0}
  35. li{margin:4px 0;font-size:13px;line-height:1.7}
  36. .toc{background:#fff;border-radius:8px;padding:16px 24px;margin-bottom:16px;box-shadow:0 1px 4px rgba(0,0,0,0.08)}
  37. .toc h2{font-size:16px;color:#1a1a2e;margin-bottom:10px;border-bottom:2px solid #667eea;padding-bottom:6px}
  38. .toc ol{padding-left:20px;columns:2;column-gap:30px}
  39. .toc li{margin:4px 0;font-size:13px;break-inside:avoid}
  40. .toc a{color:#667eea;text-decoration:none}
  41. .toc a:hover{text-decoration:underline}
  42. @media(max-width:768px){
  43. .navbar{flex-wrap:wrap;gap:8px;padding:10px 14px}
  44. .navbar .brand{font-size:14px;width:100%}
  45. .navbar a{font-size:12px;padding:4px 8px}
  46. .container{padding:0 10px;margin:10px auto}
  47. .toc ol{columns:1}
  48. table{font-size:12px;display:block;overflow-x:auto}
  49. th,td{padding:6px 8px}
  50. td:first-child{font-size:11px}
  51. }
  52. @media(max-width:480px){
  53. .navbar .brand{font-size:13px}
  54. .navbar a{font-size:11px;padding:3px 6px}
  55. .card-body{padding:12px 14px}
  56. }
  57. </style>
  58. </head>
  59. <body>
  60. <nav class="navbar">
  61. <span class="brand">🚛 车牌识别系统</span>
  62. <a href="/">监控首页</a>
  63. <a href="/video">视频预览</a>
  64. <a href="/locks">锁定管理</a>
  65. <a href="/help" class="active">帮助手册</a>
  66. </nav>
  67. <div class="container">
  68. <div class="toc">
  69. <h2>📋 目录</h2>
  70. <ol>
  71. <li><a href="#sec1">系统概述</a></li>
  72. <li><a href="#sec2">快速部署指南</a></li>
  73. <li><a href="#sec3">config.ini 完整配置说明</a></li>
  74. <li><a href="#sec4">RTSP 摄像头配置</a></li>
  75. <li><a href="#sec5">车牌识别上传流程</a></li>
  76. <li><a href="#sec6">MQTT 数据上报配置</a></li>
  77. <li><a href="#sec7">称重系统对接</a></li>
  78. <li><a href="#sec8">ROI 区域识别配置</a></li>
  79. <li><a href="#sec9">交替锁定机制</a></li>
  80. <li><a href="#sec10">登录认证与权限</a></li>
  81. <li><a href="#sec11">frpc.toml 内网穿透配置详解</a></li>
  82. <li><a href="#sec12">Nginx 反向代理配置详解</a></li>
  83. <li><a href="#sec13">SSL/HTTPS 证书配置</a></li>
  84. <li><a href="#sec14">系统监控与日志</a></li>
  85. <li><a href="#sec15">Web 管理功能</a></li>
  86. <li><a href="#sec16">数据库与备份</a></li>
  87. <li><a href="#sec17">常见问题 FAQ</a></li>
  88. </ol>
  89. </div>
  90. <!-- 1. 系统概述 -->
  91. <div class="card" id="sec1">
  92. <div class="card-title" onclick="toggleCard(this)">1. 系统概述 <span class="arrow">▼</span></div>
  93. <div class="card-body">
  94. <h3>系统简介</h3>
  95. <p>车牌识别系统(PlateRecApp)是一款基于 HyperLPR3 的实时车牌识别应用,运行在树莓派 5 (aarch64) 上,支持:</p>
  96. <ul>
  97. <li>多路 RTSP 摄像头实时抓拍识别(进场/出场/侧面进/侧面出)</li>
  98. <li>车牌识别结果自动上传至云端 API</li>
  99. <li>称重系统对接(TCP 协议),自动关联车牌与称重数据</li>
  100. <li>MQTT 协议实时推送识别结果</li>
  101. <li>飞书群消息通知(异常报警)</li>
  102. <li>Web 管理界面(监控首页、视频预览、系统配置、锁定管理等)</li>
  103. <li>frpc 内网穿透管理 + Nginx stream + ssl_preread 协议分流(HTTP 自动跳 HTTPS)</li>
  104. <li>登录认证与角色权限控制</li>
  105. <li>SD 卡写入寿命保护(内存缓冲 + 每日定时写盘)</li>
  106. </ul>
  107. <h3>运行环境</h3>
  108. <table>
  109. <tr><th>项目</th><th>要求</th></tr>
  110. <tr><td>硬件</td><td>树莓派 5 8GB(推荐)</td></tr>
  111. <tr><td>系统</td><td>Debian 13 (trixie) / Ubuntu 24.04 aarch64</td></tr>
  112. <tr><td>依赖</td><td>HyperLPR3 SDK、FFmpeg、libcurl、OpenSSL、cJSON、Mosquitto、Nginx</td></tr>
  113. <tr><td>内网穿透</td><td>frpc(通过 systemctl 管理)</td></tr>
  114. <tr><td>协议分流</td><td>Nginx stream + ssl_preread(TCP 层 HTTP/HTTPS 自动检测分流)</td></tr>
  115. </table>
  116. <h3>网络架构总览(v2 — nginx stream + ssl_preread 方案)</h3>
  117. <pre>外网客户端 frps 服务器 本机(树莓派5)
  118. ───────── ───────── ─────────────
  119. ┌─ TLS → nginx stream → C++ HTTPS:8080 (透传)
  120. https://mqtt.zhonjin.com:40963/ ─TCP─→ discover.zhonjin.com:9443 ─TCP─→ frpc ─→ nginx:40963 (stream+ssl_preread)
  121. http://mqtt.zhonjin.com:40963/ ─TCP─→ ↑ ─TCP─→ frpc ─→└─ HTTP → nginx:8081 (301→HTTPS)
  122. https://mqtt.zhonjin.com:40964/ ─TCP─→ ↑ ─TCP─→ frpc ─→ nginx:8082 (HTTPS代理) → ttyd:7681</pre>
  123. <div class="tip"><strong>v2 方案优势:</strong>公网只需一个端口 40963,nginx stream 在 TCP 层自动检测协议类型并分流。HTTP 自动跳 HTTPS,HTTPS 透传到 C++ 应用处理。用户只需记住一个地址。</div>
  124. <h3>目录结构</h3>
  125. <table>
  126. <tr><th>路径</th><th>说明</th></tr>
  127. <tr><td>PlateRecApp</td><td>主程序二进制</td></tr>
  128. <tr><td>config.ini</td><td>主配置文件</td></tr>
  129. <tr><td>config.txt</td><td>MD5 校验配置</td></tr>
  130. <tr><td>hyperlpr3/</td><td>HyperLPR3 SDK 模型文件</td></tr>
  131. <tr><td>assets/web/</td><td>Web 页面文件(HTML/CSS/JS)</td></tr>
  132. <tr><td>data/</td><td>数据库目录(auth.db、upload_records.db、system_metrics.db)</td></tr>
  133. <tr><td>photos/</td><td>抓拍图片存储目录</td></tr>
  134. <tr><td>scripts/</td><td>运维脚本(sudoers 配置、密码重置等)</td></tr>
  135. </table>
  136. </div>
  137. </div>
  138. <!-- 2. 快速部署指南 -->
  139. <div class="card" id="sec2">
  140. <div class="card-title" onclick="toggleCard(this)">2. 快速部署指南 <span class="arrow">▼</span></div>
  141. <div class="card-body">
  142. <h3>编译部署</h3>
  143. <ol>
  144. <li>解压代码包:<code>tar xzf fix24_vXX.tar.gz</code></li>
  145. <li>编译:<code>cd fix24/build && cmake .. && make -j4</code></li>
  146. <li>部署文件到安装目录(如 <code>/opt/openAI/project/003.PlateRecAPP/</code>)</li>
  147. <li>复制 HTML 文件:<code>cp -r assets/web/ /安装目录/assets/web/</code></li>
  148. <li>配置 sudoers(frpc 管理需要):<code>sudo bash scripts/setup_frpc_sudoers.sh</code></li>
  149. <li>安装 nginx stream 模块:<code>sudo apt install libnginx-mod-stream</code></li>
  150. <li>部署 nginx stream 配置文件(详见第 12 章)</li>
  151. <li>部署 SSL 证书(C++ 应用自管理,详见第 13 章)</li>
  152. <li>修改 <code>config.ini</code> 配置(参考下文各节)</li>
  153. <li>启动服务:<code>sudo systemctl restart nginx && sudo systemctl restart PlateRecApp</code></li>
  154. </ol>
  155. <h3>systemd 服务配置</h3>
  156. <p>PlateRecApp 服务文件路径:<code>/etc/systemd/system/PlateRecApp.service</code></p>
  157. <pre>Unit]
  158. Description=PlateRecApp License Plate Recognition System
  159. After=network.target frpc.service
  160. [Service]
  161. User=stevenroc
  162. Group=stevenroc
  163. WorkingDirectory=/opt/openAI/project/003.PlateRecAPP
  164. ExecStartPre=/bin/sleep 10
  165. ExecStart=/opt/openAI/project/003.PlateRecAPP/PlateRecApp
  166. TimeoutStartSec=30
  167. TimeoutStopSec=10
  168. Restart=on-failure
  169. RestartSec=5
  170. StandardOutput=append:/opt/openAI/project/003.PlateRecAPP/PlateRecApp.log
  171. LimitNOFILE=1048576
  172. [Install]
  173. WantedBy=multi-user.target</pre>
  174. <div class="tip">PlateRecApp 以非 root 用户运行(stevenroc),frpc 管理通过 sudoers 授权。日志输出由 v36 内存缓冲系统接管,systemd 的 StandardOutput 仅作备份。</div>
  175. <h3>密码重置</h3>
  176. <p>如忘记管理员密码,运行:<code>python3 reset_passwords.py</code></p>
  177. <h3>Nginx 服务配置(v2 — stream 方案)</h3>
  178. <p>安装 stream 模块并部署配置:</p>
  179. <pre># 安装 stream 模块(必须)
  180. sudo apt install -y libnginx-mod-stream
  181. # 验证模块
  182. nginx -V 2>&amp;1 | grep stream
  183. # 替换 nginx.conf(详见第 12 章)
  184. sudo cp nginx-stream.conf /etc/nginx/nginx.conf
  185. # 验证配置
  186. sudo nginx -t
  187. # 启动/重载
  188. sudo systemctl enable nginx
  189. sudo systemctl restart nginx</pre>
  190. </div>
  191. </div>
  192. <!-- 3. config.ini 完整配置 -->
  193. <div class="card" id="sec3">
  194. <div class="card-title" onclick="toggleCard(this)">3. config.ini 完整配置说明 <span class="arrow">▼</span></div>
  195. <div class="card-body">
  196. <p>配置文件路径:<code>&lt;安装目录&gt;/config.ini</code>,ini 格式,section 和 key 均不区分大小写。</p>
  197. <h3>[config] 核心识别配置</h3>
  198. <table>
  199. <tr><th>参数</th><th>类型</th><th>默认值</th><th>说明</th></tr>
  200. <tr><td>project_name</td><td>string</td><td>-</td><td>项目名称</td></tr>
  201. <tr><td>point_number</td><td>string</td><td>-</td><td>站点编号</td></tr>
  202. <tr><td>throughway</td><td>string</td><td>-</td><td>通道名称</td></tr>
  203. <tr><td>rtsp_url_front_in</td><td>string</td><td>-</td><td>前侧进场摄像头 RTSP 地址</td></tr>
  204. <tr><td>rtsp_url_front_out</td><td>string</td><td>-</td><td>前侧出场摄像头 RTSP 地址</td></tr>
  205. <tr><td>rtsp_url_side_in</td><td>string</td><td>-</td><td>侧面进场摄像头 RTSP 地址</td></tr>
  206. <tr><td>rtsp_url_side_out</td><td>string</td><td>-</td><td>侧面出场摄像头 RTSP 地址</td></tr>
  207. <tr><td>app_api</td><td>string</td><td>-</td><td>云端上传 API 地址</td></tr>
  208. <tr><td>app_key</td><td>string</td><td>-</td><td>API 认证 Key</td></tr>
  209. <tr><td>app_secret</td><td>string</td><td>-</td><td>API 认证 Secret</td></tr>
  210. <tr><td>DEBUG_LOG</td><td>int</td><td>0</td><td>调试日志开关(0=关闭,1=开启)</td></tr>
  211. <tr><td>TestFlag</td><td>int</td><td>0</td><td>测试模式开关</td></tr>
  212. <tr><td>wType</td><td>int</td><td>0</td><td>称重类型(0=禁用,1=启用)</td></tr>
  213. <tr><td>TIME_WINDOW</td><td>int</td><td>5</td><td>识别时间窗口(分钟),同一车牌在窗口内不重复上传</td></tr>
  214. <tr><td>MAX_PHOTO_GROUPS</td><td>int</td><td>100</td><td>最大照片组数缓存</td></tr>
  215. <tr><td>in_out_interval</td><td>int</td><td>5</td><td>⚠️ 单位是<strong>分钟</strong>!交替锁定间隔(同一车牌进出最短时间),内部转为秒存储</td></tr>
  216. <tr><td>PLATE_CONFIDENCE_THRESHOLD</td><td>float</td><td>0.7</td><td>车牌识别置信度阈值(0~1),低于此值不上传</td></tr>
  217. <tr><td>PLATE_LOG_THRESHOLD</td><td>float</td><td>0.5</td><td>日志记录置信度阈值</td></tr>
  218. <tr><td>AlternatingMerge</td><td>int</td><td>0</td><td>交替合并开关(0=关闭,1=开启)</td></tr>
  219. <tr><td>hw_decode_mode</td><td>string</td><td>auto</td><td>硬件解码模式:auto/soft/drm</td></tr>
  220. <tr><td>hw_decode_device</td><td>string</td><td>/dev/dri/renderD128</td><td>硬件解码设备路径</td></tr>
  221. <tr><td>rtsp_transport</td><td>string</td><td>tcp</td><td>RTSP 传输协议:tcp/udp</td></tr>
  222. <tr><td>PhotoMaxCapacityMB</td><td>int</td><td>1024</td><td>照片目录最大容量(MB),超限自动清理旧照片</td></tr>
  223. <tr><td>LogRetentionDays</td><td>int</td><td>30</td><td>日志保留天数</td></tr>
  224. </table>
  225. <h3>[server] Web 服务配置</h3>
  226. <table>
  227. <tr><th>参数</th><th>说明</th></tr>
  228. <tr><td>ip</td><td>Web 服务监听地址(含端口),如 <code>0.0.0.0:8080</code>。v2 方案下 PlateRecApp 提供 HTTPS 服务(SSL 自管理),nginx stream 透传 TLS 流量到此处</td></tr>
  229. </table>
  230. <h3>[mqtt] MQTT 上报配置</h3>
  231. <table>
  232. <tr><th>参数</th><th>默认值</th><th>说明</th></tr>
  233. <tr><td>MQTT_HOST</td><td>-</td><td>MQTT Broker 地址(填写后自动启用 MQTT)</td></tr>
  234. <tr><td>MQTT_PORT</td><td>1883</td><td>MQTT Broker 端口</td></tr>
  235. <tr><td>MQTT_USER</td><td>-</td><td>MQTT 用户名</td></tr>
  236. <tr><td>MQTT_PASS</td><td>-</td><td>MQTT 密码</td></tr>
  237. <tr><td>MQTT_TOPIC</td><td>-</td><td>推送主题</td></tr>
  238. <tr><td>MQTT_CLIENT_ID</td><td>-</td><td>客户端 ID</td></tr>
  239. </table>
  240. <h3>[auth] 登录认证配置</h3>
  241. <table>
  242. <tr><th>参数</th><th>默认值</th><th>说明</th></tr>
  243. <tr><td>enabled</td><td>1</td><td>认证开关(0=关闭,1=开启)</td></tr>
  244. <tr><td>db_path</td><td>data/auth.db</td><td>认证数据库路径</td></tr>
  245. <tr><td>session_idle_timeout</td><td>1800</td><td>Session 空闲超时(秒)</td></tr>
  246. <tr><td>session_max_timeout</td><td>28800</td><td>Session 最大超时(秒)</td></tr>
  247. <tr><td>session_remember_timeout</td><td>604800</td><td>记住登录超时(秒)</td></tr>
  248. <tr><td>max_failed_attempts</td><td>5</td><td>最大登录失败次数</td></tr>
  249. <tr><td>lockout_duration</td><td>900</td><td>锁定时长(秒)</td></tr>
  250. </table>
  251. <h3>[ssl] HTTPS 配置(v2 — C++ 应用自管理)</h3>
  252. <table>
  253. <tr><th>参数</th><th>默认值</th><th>说明</th></tr>
  254. <tr><td>enabled</td><td>1</td><td>HTTPS 开关。v2 方案下必须设为 1(C++ 应用直接处理 TLS)</td></tr>
  255. <tr><td>cert_path</td><td>ssl/plate_fullchain.pem</td><td>SSL 证书路径(相对于安装目录)</td></tr>
  256. <tr><td>key_path</td><td>ssl/plate_privkey.pem</td><td>SSL 私钥路径(相对于安装目录)</td></tr>
  257. </table>
  258. <div class="note">v2 方案变更:nginx stream 只做 TCP 分流,不解密 TLS。SSL 证书由 C++ 应用自己管理,<code>enabled</code> 必须为 1。</div>
  259. <h3>[system] 系统监控配置(v38 内存缓冲)</h3>
  260. <table>
  261. <tr><th>参数</th><th>默认值</th><th>说明</th></tr>
  262. <tr><td>monitor_interval</td><td>2</td><td>监控采集间隔(秒),delta 法计算 CPU 使用率</td></tr>
  263. <tr><td>temp_alert_threshold</td><td>70</td><td>温度告警阈值(°C)</td></tr>
  264. <tr><td>cpu_alert_threshold</td><td>90</td><td>CPU 告警阈值(%)</td></tr>
  265. <tr><td>db_path</td><td>data/system_metrics.db</td><td>监控数据库路径</td></tr>
  266. <tr><td>history_retention_days</td><td>7</td><td>原始数据保留天数</td></tr>
  267. <tr><td>aggregation_retention_days</td><td>365</td><td>聚合数据保留天数</td></tr>
  268. <tr><td>flush_time</td><td>23:20</td><td>每日定时刷盘时间(HH:MM)</td></tr>
  269. </table>
  270. <h3>[frpc] 内网穿透配置</h3>
  271. <table>
  272. <tr><th>参数</th><th>默认值</th><th>说明</th></tr>
  273. <tr><td>config_path</td><td>-</td><td>frpc.toml 配置文件路径</td></tr>
  274. <tr><td>protected_tunnels</td><td>-</td><td>受保护隧道名称(逗号分隔)</td></tr>
  275. <tr><td>auto_rollback</td><td>1</td><td>重启失败自动回滚(0/1)</td></tr>
  276. <tr><td>admin_addr</td><td>127.0.0.1</td><td>frpc Admin API 地址</td></tr>
  277. <tr><td>admin_port</td><td>7400</td><td>frpc Admin API 端口</td></tr>
  278. <tr><td>admin_user</td><td>-</td><td>frpc Admin 用户名</td></tr>
  279. <tr><td>admin_password</td><td>-</td><td>frpc Admin 密码</td></tr>
  280. </table>
  281. <h3>[log] 日志配置(v36 内存缓冲)</h3>
  282. <table>
  283. <tr><th>参数</th><th>默认值</th><th>说明</th></tr>
  284. <tr><td>enabled</td><td>1</td><td>内存缓冲模式(1=内存缓冲,0=直写文件)</td></tr>
  285. <tr><td>log_file</td><td>PlateRecApp.log</td><td>日志文件路径</td></tr>
  286. <tr><td>buffer_size_mb</td><td>20</td><td>内存缓冲区上限(MB)</td></tr>
  287. <tr><td>flush_time</td><td>23:20</td><td>每日定时刷盘时间(HH:MM)</td></tr>
  288. <tr><td>retention_days</td><td>30</td><td>日志文件保留天数</td></tr>
  289. <tr><td>redirect_stdout</td><td>1</td><td>捕获 stdout/stderr(0/1)</td></tr>
  290. <tr><td>auto_flush_on_exit</td><td>1</td><td>退出时立即刷盘(0/1)</td></tr>
  291. </table>
  292. </div>
  293. </div>
  294. <!-- 4. RTSP 摄像头配置 -->
  295. <div class="card" id="sec4">
  296. <div class="card-title" onclick="toggleCard(this)">4. RTSP 摄像头配置 <span class="arrow">▼</span></div>
  297. <div class="card-body">
  298. <h3>摄像头地址格式</h3>
  299. <p>标准 RTSP 地址格式:</p>
  300. <pre>rtsp://用户名:密码@摄像头IP:554/stream</pre>
  301. <h3>四路摄像头说明</h3>
  302. <table>
  303. <tr><th>参数</th><th>方向</th><th>说明</th></tr>
  304. <tr><td>rtsp_url_front_in</td><td>前→进</td><td>前方摄像头,捕捉进场车辆</td></tr>
  305. <tr><td>rtsp_url_front_out</td><td>前→出</td><td>前方摄像头,捕捉出场车辆</td></tr>
  306. <tr><td>rtsp_url_side_in</td><td>侧→进</td><td>侧面摄像头,捕捉进场车辆</td></tr>
  307. <tr><td>rtsp_url_side_out</td><td>侧→出</td><td>侧面摄像头,捕捉出场车辆</td></tr>
  308. </table>
  309. <h3>硬件解码(树莓派5推荐配置)</h3>
  310. <table>
  311. <tr><th>模式</th><th>值</th><th>说明</th></tr>
  312. <tr><td>自动</td><td>auto</td><td>优先硬件解码,不可用则回退软件</td></tr>
  313. <tr><td>软件解码</td><td>soft</td><td>CPU 解码,兼容性最好</td></tr>
  314. <tr><td>DRM 硬件</td><td>drm</td><td>DRM 硬件加速(树莓派5推荐)</td></tr>
  315. </table>
  316. <div class="tip">树莓派 5 推荐:<code>hw_decode_mode=drm</code>,<code>hw_decode_device=/dev/dri/renderD128</code>,<code>rtsp_transport=tcp</code></div>
  317. </div>
  318. </div>
  319. <!-- 5. 车牌识别上传流程 -->
  320. <div class="card" id="sec5">
  321. <div class="card-title" onclick="toggleCard(this)">5. 车牌识别上传完整流程 <span class="arrow">▼</span></div>
  322. <div class="card-body">
  323. <h3>识别流程</h3>
  324. <ol>
  325. <li><strong>摄像头抓拍</strong>:RTSP 视频流实时解码,检测到车辆运动时触发抓拍</li>
  326. <li><strong>车牌识别</strong>:HyperLPR3 引擎识别车牌号,返回车牌文本 + 置信度</li>
  327. <li><strong>置信度过滤</strong>:低于 <code>PLATE_CONFIDENCE_THRESHOLD</code> 则丢弃</li>
  328. <li><strong>时间窗口去重</strong>:同一车牌在 <code>TIME_WINDOW</code> 分钟内不重复上传</li>
  329. <li><strong>交替锁定检查</strong>:同一车牌进出间隔低于 <code>in_out_interval</code> 分钟则锁定</li>
  330. <li><strong>ROI 过滤</strong>:如启用 ROI,只识别指定区域内的车牌</li>
  331. <li><strong>数据上传</strong>:HTTP POST 将识别结果 + 照片上传至 <code>app_api</code></li>
  332. <li><strong>MQTT 推送</strong>:如启用 MQTT,同步推送识别结果</li>
  333. <li><strong>飞书通知</strong>:异常报警发送飞书群消息</li>
  334. </ol>
  335. </div>
  336. </div>
  337. <!-- 6. MQTT -->
  338. <div class="card" id="sec6">
  339. <div class="card-title" onclick="toggleCard(this)">6. MQTT 数据上报配置 <span class="arrow">▼</span></div>
  340. <div class="card-body">
  341. <h3>启用条件</h3>
  342. <p>在 <code>[mqtt]</code> 段中填写 <code>MQTT_HOST</code> 即自动启用 MQTT 推送。</p>
  343. <h3>配置示例</h3>
  344. <pre>[mqtt]
  345. MQTT_HOST=mqtt.example.com
  346. MQTT_PORT=1883
  347. MQTT_USER=username
  348. MQTT_PASS=password
  349. MQTT_TOPIC=plate/recognition
  350. MQTT_CLIENT_ID=plate_rec_station_01</pre>
  351. <div class="warn">MQTT 连接失败时系统会自动重连,不影响识别和上传功能。</div>
  352. </div>
  353. </div>
  354. <!-- 7. 称重系统 -->
  355. <div class="card" id="sec7">
  356. <div class="card-title" onclick="toggleCard(this)">7. 称重系统对接 <span class="arrow">▼</span></div>
  357. <div class="card-body">
  358. <h3>工作原理</h3>
  359. <p>称重系统通过 TCP 协议与称重地磅通信,车牌识别成功后自动读取称重数据并关联上传。</p>
  360. <h3>重量判定逻辑</h3>
  361. <ul>
  362. <li>连续 <code>STABLE_SAMPLE_COUNT</code> 次读数变化 &lt; <code>STABLE_THRESHOLD_KG</code> 公斤,视为稳定</li>
  363. <li>稳定后取 <code>CANDIDATE_DATA_COUNT</code> 次读数的中值作为最终重量</li>
  364. <li>重量超过 <code>MAX_WEIGHT</code> 吨视为异常,丢弃</li>
  365. </ul>
  366. </div>
  367. </div>
  368. <!-- 8. ROI -->
  369. <div class="card" id="sec8">
  370. <div class="card-title" onclick="toggleCard(this)">8. ROI 区域识别配置 <span class="arrow">▼</span></div>
  371. <div class="card-body">
  372. <h3>配置方式</h3>
  373. <ul>
  374. <li><strong>Web 可视化</strong>:首页连接状态面板中拖拽设置 ROI 区域</li>
  375. <li><strong>手动配置</strong>:在 config.ini 的 <code>[config]</code> 段填写坐标值</li>
  376. </ul>
  377. <h3>参数说明</h3>
  378. <table>
  379. <tr><th>参数</th><th>说明</th></tr>
  380. <tr><td>roi_in_x/y/w/h</td><td>进场 ROI 区域坐标和尺寸(像素)</td></tr>
  381. <tr><td>roi_in_enabled</td><td>1=启用进场 ROI</td></tr>
  382. <tr><td>roi_out_x/y/w/h</td><td>出场 ROI 区域坐标和尺寸</td></tr>
  383. <tr><td>roi_out_enabled</td><td>1=启用出场 ROI</td></tr>
  384. <tr><td>roi_debug_enabled</td><td>1=在抓拍图上绘制 ROI 框(调试用)</td></tr>
  385. </table>
  386. </div>
  387. </div>
  388. <!-- 9. 交替锁定 -->
  389. <div class="card" id="sec9">
  390. <div class="card-title" onclick="toggleCard(this)">9. 交替锁定机制 <span class="arrow">▼</span></div>
  391. <div class="card-body">
  392. <h3>机制说明</h3>
  393. <p>防止同一车牌短时间内进出频繁触发(如车辆在门口调头)。同一车牌从"进"切换到"出"必须间隔 <code>in_out_interval</code> 分钟以上。</p>
  394. <div class="warn"><code>in_out_interval</code> 单位是<strong>分钟</strong>,不是秒!填 5 表示 5 分钟 = 300 秒。</div>
  395. </div>
  396. </div>
  397. <!-- 10. 认证与权限 -->
  398. <div class="card" id="sec10">
  399. <div class="card-title" onclick="toggleCard(this)">10. 登录认证与权限 <span class="arrow">▼</span></div>
  400. <div class="card-body">
  401. <h3>角色定义</h3>
  402. <table>
  403. <tr><th>角色</th><th>Role值</th><th>可访问页面</th></tr>
  404. <tr><td>普通用户</td><td>0</td><td>首页、视频、称重、锁定管理</td></tr>
  405. <tr><td>管理员</td><td>1</td><td>普通用户 + 系统配置</td></tr>
  406. <tr><td>超级管理员</td><td>2</td><td>所有页面(含系统监控、frpc管理、SSH密钥、日志、帮助)</td></tr>
  407. </table>
  408. <h3>默认账号</h3>
  409. <table>
  410. <tr><th>用户名</th><th>密码</th><th>角色</th></tr>
  411. <tr><td>admin</td><td>zhongjin188A</td><td>超级管理员</td></tr>
  412. <tr><td>zhonjin</td><td>zhonjin</td><td>普通用户</td></tr>
  413. </table>
  414. <h3>安全特性</h3>
  415. <ul>
  416. <li>密码 bcrypt 加密存储(cost=12)</li>
  417. <li>连续登录失败锁定账号</li>
  418. <li>CSRF Token 防护</li>
  419. <li>Session 超时自动失效</li>
  420. <li>登录速率限制</li>
  421. </ul>
  422. </div>
  423. </div>
  424. <!-- 11. frpc.toml 详解 -->
  425. <div class="card" id="sec11">
  426. <div class="card-title" onclick="toggleCard(this)">11. frpc.toml 内网穿透配置详解 <span class="arrow">▼</span></div>
  427. <div class="card-body">
  428. <h3>配置文件位置</h3>
  429. <p>frpc 配置文件路径由 <code>config.ini</code> 的 <code>[frpc] config_path</code> 指定,通常为安装目录下的 <code>frpc.toml</code>。</p>
  430. <h3>v2 方案 — nginx stream 分流(推荐)</h3>
  431. <p>使用 nginx stream + ssl_preread 方案,<strong>只需一个公网端口 40963</strong> 即可同时处理 HTTP 和 HTTPS:</p>
  432. <pre># frpc.toml — v2 方案(nginx stream 分流)
  433. serverAddr = "discover.zhonjin.com"
  434. serverPort = 9443
  435. auth.method = "token"
  436. auth.token = "你的frps认证Token"
  437. transport.tls.enable = true
  438. clientID = "ssd_40960"
  439. # frpc Admin HTTP API
  440. webServer.addr = "127.0.0.1"
  441. webServer.port = 7400
  442. webServer.user = "admin"
  443. webServer.password = "你的管理密码"
  444. # 隧道1:主服务(外网40963→本机nginx:40963,stream自动分流HTTP/HTTPS)
  445. [[proxies]]
  446. name = "plate_web"
  447. type = "tcp"
  448. localIP = "127.0.0.1"
  449. localPort = 40963 # ← 转发到 nginx stream,不是 C++ 的 8080
  450. remotePort = 40963
  451. # 隧道2:ttyd终端(外网40964→本机nginx:8082→ttyd:7681)
  452. [[proxies]]
  453. name = "ttyd_web"
  454. type = "tcp"
  455. localIP = "127.0.0.1"
  456. localPort = 8082 # ← nginx HTTPS 反向代理(SSL 终止 + WebSocket)
  457. remotePort = 40964</pre>
  458. <h3>隧道说明(v2)</h3>
  459. <table>
  460. <tr><th>隧道名</th><th>外网端口</th><th>本机端口</th><th>用途</th></tr>
  461. <tr><td>plate_web</td><td>40963</td><td>nginx stream:40963</td><td>HTTP/HTTPS 合一,stream 自动分流</td></tr>
  462. <tr><td>ttyd_web</td><td>40964</td><td>nginx HTTPS:8082</td><td>Web 终端(nginx SSL→ttyd:7681)</td></tr>
  463. </table>
  464. <h3>v2 数据流向</h3>
  465. <pre>HTTP 流量: 公网:40963 → frpc → nginx:40963 (stream) → $ssl_preread="" → 127.0.0.1:8081 (nginx HTTP) → 301 重定向到 HTTPS
  466. HTTPS 流量: 公网:40963 → frpc → nginx:40963 (stream) → $ssl_preread≠"" → 127.0.0.1:8080 (C++ HTTPS) → 直接处理
  467. Web SSH: 公网:40964 → frpc → nginx:8082 (HTTPS反向代理, SSL终止) → 127.0.0.1:7681 (ttyd HTTP)</pre>
  468. <div class="tip"><strong>v1→v2 迁移注意:</strong>不再需要 40965 端口。HTTPS 主服务统一使用 <code>https://mqtt.zhonjin.com:40963/</code>。</div>
  469. <h3>参数详解</h3>
  470. <table>
  471. <tr><th>参数</th><th>说明</th></tr>
  472. <tr><td>serverAddr</td><td>frps 服务器域名或 IP</td></tr>
  473. <tr><td>serverPort</td><td>frps 服务端口(默认 7000,本项目用 9443)</td></tr>
  474. <tr><td>auth.method</td><td>认证方式,固定为 <code>token</code></td></tr>
  475. <tr><td>auth.token</td><td>与 frps 约定的认证 Token</td></tr>
  476. <tr><td>webServer.addr/port</td><td>frpc Admin API 监听地址,PlateRecApp 通过此接口获取隧道状态</td></tr>
  477. <tr><td>webServer.user/password</td><td>Admin API 认证凭据(需与 config.ini [frpc] 段一致)</td></tr>
  478. <tr><td>[[proxies]] name</td><td>隧道唯一名称,PlateRecApp Web 界面按名称识别</td></tr>
  479. <tr><td>[[proxies]] type</td><td>隧道类型,本项目全部使用 <code>tcp</code>(透传 TCP 流)</td></tr>
  480. <tr><td>[[proxies]] localPort</td><td>本机目标端口,frpc 将外网流量转发到此端口</td></tr>
  481. <tr><td>[[proxies]] remotePort</td><td>frps 服务器暴露的外网端口,客户端通过此端口访问</td></tr>
  482. </table>
  483. <h3>隧道保护</h3>
  484. <p>在 <code>config.ini [frpc]</code> 段配置受保护隧道,防止 Web 界面误删关键隧道:</p>
  485. <pre>protected_tunnels = plate_web,ttyd_web</pre>
  486. <div class="warn">受保护的隧道无法通过 Web 管理界面删除。如需删除,必须先修改 config.ini 中的 <code>protected_tunnels</code> 字段。</div>
  487. <h3>frpc 管理命令</h3>
  488. <pre># 查看 frpc 状态
  489. systemctl status frpc
  490. # 重启 frpc
  491. sudo systemctl restart frpc
  492. # 查看 frpc 日志
  493. tail -f /var/log/frpc.log
  494. # 通过 Admin API 查看隧道状态
  495. curl -u admin:密码 http://127.0.0.1:7400/api/status/tcp</pre>
  496. </div>
  497. </div>
  498. <!-- 12. Nginx 反向代理详解 -->
  499. <div class="card" id="sec12">
  500. <div class="card-title" onclick="toggleCard(this)">12. Nginx stream + ssl_preread 配置详解(v2) <span class="arrow">▼</span></div>
  501. <div class="card-body">
  502. <h3>架构说明</h3>
  503. <p>v2 方案使用 nginx 的 <code>stream</code> 模块 + <code>ssl_preread</code> 功能,在 <strong>TCP 层面</strong>检测流量类型并自动分流:</p>
  504. <ul>
  505. <li><strong>端口 40963</strong>:nginx stream 监听,检测协议类型(TLS / HTTP)</li>
  506. <li><strong>TLS 流量</strong> → 透传到 C++ HTTPS 服务(127.0.0.1:8080)</li>
  507. <li><strong>HTTP 流量</strong> → nginx HTTP 模块(127.0.0.1:8081)→ 301 重定向到 HTTPS</li>
  508. </ul>
  509. <div class="tip"><strong>关键区别:</strong>stream 模块是 TCP 层代理,不解密 TLS 流量。SSL 证书由 C++ 应用自己管理,nginx 只做协议检测和分流。</div>
  510. <h3>nginx.conf 完整配置</h3>
  511. <p>文件路径:<code>/etc/nginx/nginx.conf</code>(替换整个文件)</p>
  512. <pre># 加载 stream 动态模块
  513. load_module modules/ngx_stream_module.so;
  514. events {
  515. worker_connections 1024;
  516. }
  517. # ============================================================
  518. # stream 块 — TCP 层协议分流(在 http 块外面)
  519. # ============================================================
  520. stream {
  521. # 根据 ssl_preread 检测结果选择后端
  522. # $ssl_preread_protocol 的可能值:
  523. # "" → HTTP 流量(非 TLS)
  524. # "TLSv1.2" → TLS 1.2
  525. # "TLSv1.3" → TLS 1.3
  526. map $ssl_preread_protocol $backend {
  527. "" http_backend; # HTTP → nginx HTTP 8081
  528. default tls_backend; # TLS → C++ HTTPS 8080
  529. }
  530. # TLS 后端 — C++ HTTPS 服务
  531. upstream tls_backend {
  532. server 127.0.0.1:8080;
  533. }
  534. # HTTP 后端 — nginx HTTP 301 重定向
  535. upstream http_backend {
  536. server 127.0.0.1:8081;
  537. }
  538. # 主服务器 — 监听公网端口
  539. server {
  540. listen 40963;
  541. proxy_pass $backend;
  542. ssl_preread on; # 启用 SSL 预读检测协议
  543. }
  544. }
  545. # ============================================================
  546. # http 块 — HTTP → HTTPS 301 重定向
  547. # ============================================================
  548. http {
  549. sendfile on;
  550. tcp_nopush on;
  551. tcp_nodelay on;
  552. keepalive_timeout 65;
  553. include /etc/nginx/mime.types;
  554. default_type application/octet-stream;
  555. access_log /var/log/nginx/access.log;
  556. error_log /var/log/nginx/error.log;
  557. # HTTP → HTTPS 301 重定向
  558. server {
  559. listen 8081;
  560. server_name _;
  561. return 301 https://$host:40963$request_uri;
  562. }
  563. # ============================================================
  564. # ttyd Web 终端 HTTPS 反向代理(端口 8082)
  565. # ============================================================
  566. # frpc 隧道:外网 40964 → nginx:8082 → ttyd:7681
  567. # nginx 做 SSL 终止,再通过 HTTP 代理到 ttyd(含 WebSocket 支持)
  568. server {
  569. listen 8082 ssl;
  570. server_name _;
  571. ssl_certificate /etc/nginx/ssl/plate_fullchain.pem;
  572. ssl_certificate_key /etc/nginx/ssl/plate_privkey.pem;
  573. ssl_protocols TLSv1.2 TLSv1.3;
  574. ssl_ciphers HIGH:!aNULL:!MD5:!RC4;
  575. ssl_prefer_server_ciphers on;
  576. ssl_session_cache shared:SSL_Ttyd:5m;
  577. ssl_session_timeout 1h;
  578. location / {
  579. proxy_pass http://127.0.0.1:7681;
  580. proxy_set_header Host $host;
  581. proxy_set_header X-Real-IP $remote_addr;
  582. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  583. proxy_set_header X-Forwarded-Proto $scheme;
  584. # WebSocket 支持(ttyd 核心通信方式)
  585. proxy_http_version 1.1;
  586. proxy_set_header Upgrade $http_upgrade;
  587. proxy_set_header Connection "upgrade";
  588. # ttyd 长连接超时
  589. proxy_read_timeout 86400s;
  590. proxy_send_timeout 86400s;
  591. }
  592. }
  593. }</pre>
  594. <h3>核心原理</h3>
  595. <table>
  596. <tr><th>组件</th><th>作用</th><th>说明</th></tr>
  597. <tr><td>stream 块</td><td>TCP 层代理</td><td>与 http 块平级,不处理 HTTP 协议,只做 TCP 转发</td></tr>
  598. <tr><td>ssl_preread on</td><td>SSL 预读</td><td>在 TLS 握手阶段读取 ClientHello,获取协议版本,不解密</td></tr>
  599. <tr><td>map $ssl_preread_protocol</td><td>协议分流</td><td>空字符串 = HTTP,非空 = TLS</td></tr>
  600. <tr><td>upstream tls_backend</td><td>TLS 后端</td><td>透传到 C++ 应用(8080),C++ 自己处理 TLS 握手</td></tr>
  601. <tr><td>upstream http_backend</td><td>HTTP 后端</td><td>转发到 nginx HTTP 8081,返回 301 重定向</td></tr>
  602. <tr><td>server 8082 ssl</td><td>ttyd HTTPS 代理</td><td>SSL 终止后代理到 ttyd:7681,支持 WebSocket</td></tr>
  603. </table>
  604. <h3>部署步骤</h3>
  605. <ol>
  606. <li>安装 stream 模块:<code>sudo apt install -y libnginx-mod-stream</code></li>
  607. <li>验证模块:<code>ls -l /usr/lib/nginx/modules/ngx_stream_module.so</code></li>
  608. <li>备份原配置:<code>sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak</code></li>
  609. <li>替换配置:<code>sudo cp nginx-stream.conf /etc/nginx/nginx.conf</code></li>
  610. <li>删除默认站点(如有):<code>sudo rm -f /etc/nginx/sites-enabled/default</code></li>
  611. <li>验证配置:<code>sudo nginx -t</code></li>
  612. <li>启动 nginx:<code>sudo systemctl enable nginx && sudo systemctl restart nginx</code></li>
  613. </ol>
  614. <h3>前置条件</h3>
  615. <table>
  616. <tr><th>项目</th><th>要求</th></tr>
  617. <tr><td>nginx 版本</td><td>≥ 1.11.5(支持 stream 模块)</td></tr>
  618. <tr><td>stream 模块</td><td>libnginx-mod-stream(Debian)或 nginx-mod-stream(CentOS)</td></tr>
  619. <tr><td>C++ 应用</td><td>必须启用 HTTPS,监听 127.0.0.1:8080</td></tr>
  620. <tr><td>SSL 证书</td><td>C++ 应用自行管理(config.ini 的 [ssl] 段)</td></tr>
  621. </table>
  622. <h3>验证方法</h3>
  623. <pre># 检查端口监听
  624. ss -tlnp | grep -E '40963|8080|8081'
  625. # 预期输出:
  626. # LISTEN 0.0.0.0:40963 nginx (stream)
  627. # LISTEN 127.0.0.1:8081 nginx (http 301)
  628. # LISTEN 0.0.0.0:8080 PlateRecApp (HTTPS)
  629. # 测试 HTTP 重定向(应返回 301)
  630. curl -I http://localhost:40963/
  631. # 预期:HTTP/1.1 301 Moved Permanently
  632. # Location: https://localhost:40963/
  633. # 测试 HTTPS(应返回 200)
  634. curl -kI https://localhost:40963/
  635. # 预期:HTTP/1.1 200 OK
  636. # 浏览器测试(清除缓存或用无痕模式)
  637. # 访问 http://mqtt.zhonjin.com:40963/ → 自动跳转到 https://mqtt.zhonjin.com:40963/</pre>
  638. <h3>端口规划总结</h3>
  639. <table>
  640. <tr><th>端口</th><th>服务</th><th>说明</th></tr>
  641. <tr><td><strong>40963</strong></td><td>nginx stream</td><td>公网入口,TLS/HTTP 分流</td></tr>
  642. <tr><td>8081</td><td>nginx http</td><td>HTTP 301 重定向(仅内部)</td></tr>
  643. <tr><td>8080</td><td>C++ HTTPS</td><td>PlateRecApp Web 服务(SSL 自管理)</td></tr>
  644. <tr><td><strong>40964</strong></td><td>nginx HTTPS→ttyd</td><td>Web 终端(nginx:8082 SSL终止→ttyd:7681)</td></tr>
  645. <tr><td>8082</td><td>nginx HTTPS 代理</td><td>ttyd Web 终端 HTTPS 反向代理(仅内部)</td></tr>
  646. <tr><td>7400</td><td>frpc Admin</td><td>隧道状态查询(仅 127.0.0.1)</td></tr>
  647. </table>
  648. </div>
  649. </div>
  650. <!-- 13. SSL/HTTPS 证书配置 -->
  651. <div class="card" id="sec13">
  652. <div class="card-title" onclick="toggleCard(this)">13. SSL/HTTPS 证书配置(v2 — C++ 应用自管理) <span class="arrow">▼</span></div>
  653. <div class="card-body">
  654. <div class="note"><strong>v2 方案变更:</strong>nginx stream 只做 TCP 层协议分流,不解密 TLS 流量。SSL 证书由 C++ 应用(PlateRecApp)自己管理,通过 config.ini 的 <code>[ssl]</code> 段配置。nginx 不再需要 SSL 证书。</div>
  655. <h3>config.ini [ssl] 段配置</h3>
  656. <pre>[ssl]
  657. enabled=1
  658. cert_path=ssl/plate_fullchain.pem
  659. key_path=ssl/plate_privkey.pem</pre>
  660. <div class="tip">v2 方案下 SSL 证书文件放在安装目录的 <code>ssl/</code> 子目录下(或任意自定义路径),不再需要 <code>/etc/nginx/ssl/</code>。</div>
  661. <h3>方式一:Let's Encrypt 证书 — 阿里云 DNS 验证(推荐)</h3>
  662. <p>本项目使用 frpc 内网穿透,<strong>无公网 80 端口</strong>,因此不能使用传统的 HTTP-01 验证。推荐使用 <strong>DNS-01 验证</strong>,通过阿里云 DNS API 自动完成,<strong>不需要任何端口</strong>。</p>
  663. <h4>方案对比</h4>
  664. <table>
  665. <tr><th>验证方式</th><th>端口要求</th><th>通配符</th><th>自动续期</th><th>本项目适用</th></tr>
  666. <tr><td>HTTP-01 (standalone)</td><td>公网 80</td><td>❌</td><td>✅</td><td>❌ 80 端口不可用</td></tr>
  667. <tr><td>TLS-ALPN-01</td><td>公网 443</td><td>❌</td><td>✅</td><td>❌ 443 端口不可用</td></tr>
  668. <tr><td><strong>DNS-01 (dns-aliyun)</strong></td><td><strong>无</strong></td><td><strong>✅</strong></td><td><strong>✅</strong></td><td><strong>✅ 推荐</strong></td></tr>
  669. <tr><td>手动 DNS</td><td>无</td><td>✅</td><td>❌ 手动</td><td>⚠️ 每 90 天手动操作</td></tr>
  670. </table>
  671. <h4>一键部署</h4>
  672. <pre># 1. 安装 certbot + 阿里云 DNS 插件
  673. sudo apt update
  674. sudo apt install -y certbot python3-pip
  675. sudo pip3 install certbot-dns-aliyun
  676. # 2. 配置阿里云 AccessKey(RAM 子账号,需 AliyunDNSFullAccess 权限)
  677. sudo mkdir -p /etc/letsencrypt
  678. sudo vim /etc/letsencrypt/aliyun.ini
  679. # 内容:
  680. # dns_aliyun_access_key = 你的AccessKeyID
  681. # dns_aliyun_access_key_secret = 你的AccessKeySecret
  682. sudo chmod 600 /etc/letsencrypt/aliyun.ini
  683. # 3. 一键申请通配符证书(自动通过 DNS API 验证)
  684. sudo certbot certonly \
  685. --authenticator dns-aliyun \
  686. --dns-aliyun-credentials /etc/letsencrypt/aliyun.ini \
  687. --dns-aliyun-propagation-seconds 30 \
  688. --server https://acme-v02.api.letsencrypt.org/directory \
  689. --agree-tos --no-eff-email \
  690. --email admin@zhonjin.com \
  691. -d mqtt.zhonjin.com \
  692. -d "*.zhonjin.com"
  693. # 4. 部署证书到 PlateRecApp
  694. mkdir -p ssl/
  695. sudo cp /etc/letsencrypt/live/mqtt.zhonjin.com/fullchain.pem ssl/plate_fullchain.pem
  696. sudo cp /etc/letsencrypt/live/mqtt.zhonjin.com/privkey.pem ssl/plate_privkey.pem
  697. sudo chown stevenroc:stevenroc ssl/*.pem
  698. chmod 644 ssl/plate_fullchain.pem
  699. chmod 600 ssl/plate_privkey.pem
  700. # 5. 配置自动续期(每天 3:00 检查,续期后自动部署)
  701. (crontab -l 2>/dev/null; echo "0 3 * * * certbot renew --quiet --deploy-hook /opt/openAI/project/003.PlateRecAPP/scripts/renew_ssl.sh") | crontab -
  702. # 6. 重启 PlateRecApp
  703. sudo systemctl restart PlateRecApp</pre>
  704. <h4>续期部署脚本 renew_ssl.sh</h4>
  705. <pre>#!/bin/bash
  706. # /opt/openAI/project/003.PlateRecAPP/scripts/renew_ssl.sh
  707. DOMAIN="mqtt.zhonjin.com"
  708. SSL_DIR="/opt/openAI/project/003.PlateRecAPP/ssl"
  709. cp /etc/letsencrypt/live/${DOMAIN}/fullchain.pem ${SSL_DIR}/plate_fullchain.pem
  710. cp /etc/letsencrypt/live/${DOMAIN}/privkey.pem ${SSL_DIR}/plate_privkey.pem
  711. chown stevenroc:stevenroc ${SSL_DIR}/*.pem
  712. chmod 644 ${SSL_DIR}/plate_fullchain.pem
  713. chmod 600 ${SSL_DIR}/plate_privkey.pem
  714. systemctl restart PlateRecApp
  715. echo "$(date) SSL 证书续期部署成功" >> /opt/openAI/project/003.PlateRecAPP/ssl_renew.log</pre>
  716. <h4>阿里云 RAM 权限配置</h4>
  717. <ol>
  718. <li>登录 <a href="https://ram.console.aliyun.com/" target="_blank">RAM 控制台</a> → 用户 → 创建用户</li>
  719. <li>勾选 <strong>OpenAPI 调用访问</strong>,保存 AccessKey ID 和 Secret</li>
  720. <li>添加权限:<code>AliyunDNSFullAccess</code>(云解析 DNS 管理)</li>
  721. </ol>
  722. <div class="tip">DNS 验证原理:certbot 通过阿里云 API 自动添加 <code>_acme-challenge.mqtt.zhonjin.com</code> TXT 记录,Let's Encrypt CA 查询 DNS 验证域名所有权,全程无需开放任何端口。</div>
  723. <div class="warn"><strong>前提:</strong>域名 mqtt.zhonjin.com 的 DNS 解析必须使用阿里云(<code>dns9.hichina.com</code>)。如果 DNS 在其他服务商,需先迁移到阿里云,或改用对应插件(如 <code>certbot-dns-cloudflare</code>、<code>certbot-dns-dnspod</code>)。</div>
  724. <h3>方式二:自签名证书</h3>
  725. <p>无公网域名时使用自签名证书,浏览器会显示警告但加密通信正常:</p>
  726. <pre># 创建证书目录
  727. mkdir -p ssl/
  728. # 生成自签名证书(10年有效期)
  729. openssl req -x509 -newkey rsa:2048 -nodes \
  730. -keyout ssl/plate_privkey.pem \
  731. -out ssl/plate_fullchain.pem \
  732. -days 3650 \
  733. -subj "/CN=mqtt.zhonjin.com/O=PlateRecApp/C=CN"
  734. # 设置权限
  735. chmod 600 ssl/plate_privkey.pem
  736. chmod 644 ssl/plate_fullchain.pem
  737. # 重启 PlateRecApp 加载证书
  738. sudo systemctl restart PlateRecApp</pre>
  739. <h3>证书文件说明</h3>
  740. <table>
  741. <tr><th>文件</th><th>用途</th><th>config.ini 参数</th></tr>
  742. <tr><td>plate_fullchain.pem</td><td>完整证书链(含中间 CA)</td><td>cert_path</td></tr>
  743. <tr><td>plate_privkey.pem</td><td>私钥文件(严禁泄露)</td><td>key_path</td></tr>
  744. </table>
  745. <h3>v1→v2 迁移说明</h3>
  746. <div class="warn"><strong>v1 方案:</strong>SSL 证书由 nginx 管理(<code>/etc/nginx/ssl/</code>),nginx 做 HTTPS 终止后转发 HTTP 到 C++。<br>
  747. <strong>v2 方案:</strong>SSL 证书由 C++ 应用管理,nginx stream 只做 TCP 分流,不解密 TLS。不再需要 <code>/etc/nginx/ssl/</code> 目录。</div>
  748. </div>
  749. </div>
  750. <!-- 14. 系统监控与日志 -->
  751. <div class="card" id="sec14">
  752. <div class="card-title" onclick="toggleCard(this)">14. 系统监控与日志 <span class="arrow">▼</span></div>
  753. <div class="card-body">
  754. <h3>系统监控(实时)</h3>
  755. <ul>
  756. <li>CPU 使用率(delta 法,含每核使用率、负载均值)</li>
  757. <li>内存使用率(总量/已用/可用)</li>
  758. <li>磁盘使用率(根分区)</li>
  759. <li>CPU 温度,超阈值告警</li>
  760. <li>PlateRecApp 进程资源占用</li>
  761. <li>frpc/MQTT/称重连接状态</li>
  762. </ul>
  763. <h3>历史监控数据(v37+)</h3>
  764. <ul>
  765. <li>采集间隔:<code>monitor_interval</code>(默认 2 秒)</li>
  766. <li>定时刷盘:每日 <code>flush_time</code>(默认 23:20)写入 <code>system_metrics.db</code></li>
  767. <li>三级存储:原始数据 → 小时聚合 → 日聚合</li>
  768. <li>自动清理:原始数据保留 7 天,聚合数据保留 365 天</li>
  769. <li>历史查询 API:<code>/api/monitor/history?range=1h|6h|24h|7d|30d</code></li>
  770. </ul>
  771. <h3>SD 卡写入保护策略(v38)</h3>
  772. <ul>
  773. <li><strong>系统监控</strong>:内存缓冲,每日 23:20 一次写入,SD 卡写入从 2880 次/天降至 1 次</li>
  774. <li><strong>运行日志</strong>:内存环形缓冲区,Web 日志页面零磁盘 I/O,每日 23:20 写入 <code>PlateRecApp_YYYYMMDD.log</code></li>
  775. <li><strong>旧版日志清理</strong>:内存模式启用时自动跳过旧版 copytruncate,避免无意义读写</li>
  776. <li><strong>退出安全刷盘</strong>:SIGTERM/SIGINT 时立即执行最后一次刷盘</li>
  777. </ul>
  778. <h3>日志管理(v36 内存缓冲)</h3>
  779. <ul>
  780. <li>内存缓冲模式(默认):所有日志写入内存,Web 页面实时读取</li>
  781. <li>直写文件模式(<code>enabled=0</code>):传统模式回退</li>
  782. <li>支持手动触发刷盘(Web 页面操作)</li>
  783. </ul>
  784. </div>
  785. </div>
  786. <!-- 15. Web管理 -->
  787. <div class="card" id="sec15">
  788. <div class="card-title" onclick="toggleCard(this)">15. Web 管理功能 <span class="arrow">▼</span></div>
  789. <div class="card-body">
  790. <table>
  791. <tr><th>页面</th><th>路径</th><th>权限</th><th>说明</th></tr>
  792. <tr><td>监控首页</td><td>/</td><td>所有用户</td><td>实时监控、识别记录、连接状态</td></tr>
  793. <tr><td>视频预览</td><td>/video</td><td>所有用户</td><td>实时视频流</td></tr>
  794. <tr><td>称重记录</td><td>/weight</td><td>所有用户</td><td>称重数据</td></tr>
  795. <tr><td>锁定管理</td><td>/locks</td><td>所有用户</td><td>交替锁定记录</td></tr>
  796. <tr><td>帮助手册</td><td>/help</td><td>超级管理员</td><td>本文档</td></tr>
  797. <tr><td>系统配置</td><td>/config</td><td>管理员+</td><td>config.ini 在线编辑</td></tr>
  798. <tr><td>系统监控</td><td>/system</td><td>超级管理员</td><td>CPU/内存/温度/磁盘</td></tr>
  799. <tr><td>frpc 管理</td><td>/frpc</td><td>超级管理员</td><td>内网穿透状态和配置</td></tr>
  800. <tr><td>SSH 密钥</td><td>/ssh-keys</td><td>超级管理员</td><td>SSH 公钥管理</td></tr>
  801. <tr><td>日志管理</td><td>/logs</td><td>超级管理员</td><td>系统日志查看</td></tr>
  802. </table>
  803. </div>
  804. </div>
  805. <!-- 16. 数据库 -->
  806. <div class="card" id="sec16">
  807. <div class="card-title" onclick="toggleCard(this)">16. 数据库与备份 <span class="arrow">▼</span></div>
  808. <div class="card-body">
  809. <h3>数据库文件</h3>
  810. <table>
  811. <tr><th>文件</th><th>路径</th><th>说明</th></tr>
  812. <tr><td>认证数据库</td><td>data/auth.db</td><td>用户账号、密码、Session</td></tr>
  813. <tr><td>识别记录</td><td>data/upload_records.db</td><td>车牌识别上传记录</td></tr>
  814. <tr><td>系统监控</td><td>data/system_metrics.db</td><td>性能监控时序数据</td></tr>
  815. </table>
  816. <h3>备份建议</h3>
  817. <ul>
  818. <li>定期备份 <code>data/</code> 目录和 <code>config.ini</code></li>
  819. <li>nginx SSL 证书和配置文件备份:<code>/etc/nginx/ssl/</code>、<code>/etc/nginx/conf.d/</code></li>
  820. <li>frpc 配置修改时系统自动备份</li>
  821. </ul>
  822. </div>
  823. </div>
  824. <!-- 17. FAQ -->
  825. <div class="card" id="sec17">
  826. <div class="card-title" onclick="toggleCard(this)">17. 常见问题 FAQ <span class="arrow">▼</span></div>
  827. <div class="card-body">
  828. <h3>Q: HTTP 访问没有跳转到 HTTPS?</h3>
  829. <p>A: 检查以下几点:</p>
  830. <ul>
  831. <li>确认 nginx stream 模块已安装:<code>nginx -V 2>&amp;1 | grep stream</code></li>
  832. <li>确认 nginx 配置正确:<code>sudo nginx -t</code></li>
  833. <li>确认 8081 端口正在监听:<code>ss -tlnp | grep 8081</code></li>
  834. <li>确认 frpc 隧道 <code>plate_web</code> 正常运行(localPort 为 40963)</li>
  835. <li>浏览器可能缓存了旧的重定向,尝试清除缓存或无痕模式</li>
  836. <li>测试本机重定向:<code>curl -I http://localhost:40963/</code>,应返回 301</li>
  837. </ul>
  838. <h3>Q: HTTPS 访问返回错误?</h3>
  839. <p>A: v2 方案下 TLS 由 C++ 应用处理,检查:</p>
  840. <ul>
  841. <li>PlateRecApp 是否正在运行:<code>systemctl status PlateRecApp</code></li>
  842. <li>PlateRecApp 是否启用了 HTTPS:<code>config.ini [ssl] enabled=1</code></li>
  843. <li>SSL 证书文件是否存在:<code>ls -l ssl/plate_fullchain.pem ssl/plate_privkey.pem</code></li>
  844. <li>nginx stream 是否正常监听:<code>ss -tlnp | grep 40963</code></li>
  845. <li>nginx 错误日志:<code>sudo tail -f /var/log/nginx/error.log</code></li>
  846. </ul>
  847. <h3>Q: <code>unknown directive "stream"</code>?</h3>
  848. <p>A: stream 模块未加载。执行:<code>sudo apt install -y libnginx-mod-stream</code>,确认 nginx.conf 顶部有 <code>load_module modules/ngx_stream_module.so;</code></p>
  849. <h3>Q: frpc 重启后隧道不生效?</h3>
  850. <p>A: 检查 frpc.toml 配置语法:</p>
  851. <pre>frpc verify -c /path/to/frpc.toml</pre>
  852. <p>常见错误:TOML 格式问题(缺少引号、缩进错误)。通过 Web 管理界面编辑配置时系统会自动校验。</p>
  853. <h3>Q: 自签名证书浏览器警告怎么消除?</h3>
  854. <p>A: 自签名证书无法消除浏览器警告(这是正常安全行为)。解决方案:</p>
  855. <ul>
  856. <li>方式1:使用 Let's Encrypt 免费证书(需域名解析到公网 IP)</li>
  857. <li>方式2:在客户端将自签名 CA 证书添加到受信任的根证书列表</li>
  858. <li>方式3:忽略警告,继续使用(加密通信正常,仅证书不受信任)</li>
  859. </ul>
  860. <h3>Q: 摄像头连接失败怎么办?</h3>
  861. <p>A: 确认 RTSP 地址格式正确,在 VLC 中测试播放。尝试 <code>rtsp_transport=tcp</code>。</p>
  862. <h3>Q: in_out_interval 填 5 是 5 秒还是 5 分钟?</h3>
  863. <p>A: <strong>5 分钟</strong>。单位是分钟,系统内部自动 ×60 转为秒。</p>
  864. <h3>Q: 如何重置管理员密码?</h3>
  865. <pre>cd /opt/openAI/project/003.PlateRecAPP
  866. python3 reset_passwords.py</pre>
  867. </div>
  868. </div>
  869. </div>
  870. <script>
  871. function toggleCard(el) {
  872. el.classList.toggle('collapsed');
  873. var body = el.nextElementSibling;
  874. body.classList.toggle('hidden');
  875. }
  876. </script>
  877. </body>
  878. </html>