修改平台名称和界面,增加了项目介绍文档和部分代码注释;为申请软著增加的代码文件。

This commit is contained in:
tian 2026-06-10 14:31:55 +08:00
parent 7a2e82db0d
commit 2921c180cb
9 changed files with 4160 additions and 4 deletions

162
docs/project_info.md Normal file
View File

@ -0,0 +1,162 @@
# 工业安全生产视觉监测平台——项目信息
## 摘要
本平台由**管理后台**和**边缘计算节点**两部分组成。管理后台基于 **Go 1.23** 开发,部署于 Windows 环境,采用 SQLite 嵌入式数据库,前端使用 Tabler CSS 框架及服务端模板渲染。边缘节点运行于 **RK3588 ARM Linux**,核心分析引擎用 **C++17** 实现,通过 NPU/VPU/RGA 硬件加速进行实时视频 AI 推理;设备管理代理用 **Go 1.21** 开发,负责与管理后台通信。平台总代码量 **109,924 行**覆盖设备管理、AI 模型部署、人脸识别、劳保用品合规检测、告警联动等完整功能链路。
---
## 一、管理后台managerd
### 1. 软件开发环境/开发工具
| 类别 | 详情 |
|------|------|
| 编程语言 | Go 1.23.3 |
| IDE/编辑器 | VS Code / Cursor |
| 构建工具 | Go build + PowerShell 脚本scripts/managerd.ps1 |
| 版本控制 | Git |
| 开发操作系统 | Windows 11 |
### 2. 软件运行支撑环境/支持软件
| 类别 | 详情 |
|------|------|
| 运行时 | Go 编译为单一可执行文件managerd.exe无需额外运行时 |
| 操作系统 | Windows服务端 |
| 数据库 | SQLite嵌入式数据库modernc.org/sqlite 纯 Go 驱动) |
| Web 框架 | go-chi/chi v5.2.3HTTP 路由) |
| 前端框架 | Tabler CSS + 原生 JavaScript服务端模板渲染 |
| 浏览器 | Chrome / Edge / Firefox 等现代浏览器 |
| 依赖库 | chi路由、cors跨域、uuid标识生成 |
### 3. 编程语言
**Go**(版本 1.23.3
### 4. 代码行数
| 类型 | 文件数 | 总行数 |
|------|--------|--------|
| Go 源码 | 59 个 | 23,500 行 |
| 前端模板HTML/CSS | 62 个 | 6,675 行 |
| JSON 配置文件 | 19 个 | — |
| 核心文件 `internal/web/ui.go` | 1 个 | 4,861 行 |
### 5. 软件架构
```
cmd/managerd/main.go —— 组装入口,手动依赖注入
internal/api/ —— HTTP API 处理器chi router
internal/web/ —— Web UI 处理器 + 嵌入式前端模板
internal/service/ —— 业务逻辑层
internal/storage/ —— SQLite 数据持久层
internal/config/ —— JSON 配置文件加载
internal/models/ —— 核心领域模型
templates/ —— 本地 JSON 模板文件
```
### 6. 主要模块
| 模块 | 说明 |
|------|------|
| 仪表盘 | 设备统计、在线率、告警概览、异常设备、最近任务 |
| 管控台 | 设备能力矩阵、视频源分配、通道管理 |
| 设备管理 | 局域网设备发现、注册、在线监控、批量操作 |
| 设备控制 | 配置预览、应用下发、服务控制、模型上传 |
| 告警中心 | 告警采集、分页查询、规则中文翻译 |
| 人脸库 | 人员注册、照片管理、人脸库构建与下发 |
| 视频监控 | 多路视频推流代理HLS/RTSP |
| 任务中心 | 批量任务创建、执行、状态追踪 |
| 配置中心 | 模板/场景/视频源/第三方服务/调试参数管理 |
| 模型管理 | AI 模型概览与设备同步 |
| 场景管理 | 场景配置编辑、视频通道、通道部署 |
| 系统设置 | 数据备份恢复、服务状态、日志审计 |
---
## 二、边缘计算节点RK3588
### 1. 软件开发环境/开发工具
| 类别 | 详情 |
|------|------|
| 编程语言 | C++17媒体分析引擎、Go 1.21(设备代理) |
| 构建系统 | CMake 3.20+C++、Go buildAgent |
| 交叉编译 | Ubuntu 22.04 ARM64 工具链aarch64-linux-gnu-g++ |
| IDE/编辑器 | VS Code |
| 版本控制 | Git |
| 开发/运行平台 | Orange Pi 5 PlusRK3588、Ubuntu 22.04 |
### 2. 软件运行支撑环境/支持软件
| 类别 | 详情 |
|------|------|
| 操作系统 | Ubuntu 22.04ARM64 |
| 硬件平台 | 瑞芯微 RK35888 核 CPU + 6 TOPS NPU + VPU + RGA |
| AI 推理框架 | RKNN Toolkit 2Rockchip 官方 NPU 推理框架) |
| 硬件加速 | MPPMedia Process Platform硬编解码、RGA图像缩放/颜色转换) |
| 视频协议 | RTSP 输入、RTSP/HLS 输出 |
| 流媒体 | FFmpeg软件编解码备选 |
| 存储 | MinIO对象存储抓拍与录像片段 |
| 编译依赖 | CMake、GCC/G++ARM64 交叉编译或本地编译) |
| 进程管理 | systemdmedia-server.service、rk3588-agent.service |
### 3. 编程语言
- **C++17**(媒体分析引擎,核心 AI 推理管线)
- **Go 1.21**(设备管理代理 rk3588-agent
- **Python**(模型训练、数据预处理、模型转换工具)
### 4. 代码行数
| 组件 | 语言 | 文件数 | 行数 |
|------|------|--------|------|
| 媒体分析引擎 | C++17 | 200+ | 61,531 行 |
| 设备管理代理 | Go 1.21 | 41 个 | 7,251 行 |
| 工具脚本 | Python | 51 个 | 10,967 行 |
| **合计** | | **300+** | **79,749 行** |
### 5. 软件架构
```
agent/ —— Go 设备代理HTTP API、进程管理、度量采集
internal/
httpapi/ —— REST API 服务(端口 9100
mediaserver/ —— 媒体服务管理(启动/停止/状态)
metrics/ —— CPU/NPU/内存/磁盘指标采集
modelstore/ —— AI 模型文件管理
procctl/ —— 进程控制
tasks/ —— 任务执行
src/ —— C++ 媒体分析引擎
plugins/ —— AI 分析插件(人脸检测/识别、人员检测/跟踪、工鞋检测等)
ai_scrfd_sliding/ —— 滑窗人脸检测
ai_face_recog/ —— 人脸识别
ai_yolo/ —— YOLO 目标检测
ai_shoe_det/ —— 劳保鞋检测
tracker/ —— ByteTrack 多目标跟踪
logic_gate/ —— 业务规则(人鞋关联、颜色判定)
alarm/ —— 告警上报
osd/ —— 画面叠加
publish/ —— 视频推流
```
### 6. AI 分析管线
| 管线 | 说明 |
|------|------|
| 人脸识别 | RTSP 输入 → 预处理 → SCRFD 滑窗人脸检测 → MobileFaceNet 人脸识别 → OSD → HLS/RTSP 输出 |
| 劳保鞋检测 | RTSP 输入 → YOLO 人员检测 → ByteTrack 跟踪 → 动态 ROI 鞋检测 → 人鞋关联 → 颜色判定 → 告警 |
| 全功能车间 | 人脸检测 + 人脸识别 + 人员检测 + 跟踪 + 劳保鞋合规 + OSD + 视频发布 + 告警上报 + 抓拍存证 |
---
## 三、系统总览
| 项目 | 管理后台 | 边缘节点 | 合计 |
|------|----------|----------|------|
| 语言 | Go | C++17 + Go + Python | 3 种 |
| 代码行数 | 30,175 行 | 79,749 行 | **109,924 行** |
| 文件数 | 140 个 | 300+ 个 | **440+ 个** |
| 运行平台 | Windows | Ubuntu ARM64 | — |
| 数据库 | SQLite | — | SQLite |

1912
docs/source_code_p1_35.txt Normal file

File diff suppressed because it is too large Load Diff

1924
docs/source_code_p31_65.txt Normal file

File diff suppressed because it is too large Load Diff

132
extract_src.go Normal file
View File

@ -0,0 +1,132 @@
//go:build ignore
package main
import (
"bufio"
"fmt"
"os"
"regexp"
"strings"
)
// Module definitions: line number ranges and their descriptions
type Module struct {
Start int // inclusive 1-indexed
End int // inclusive 1-indexed
Title string
Comment string
}
var modules = []Module{
{1, 26, "包声明与依赖导入", "程序入口包声明及标准库、项目内部包、第三方库导入"},
{27, 48, "核心数据结构 UI", "UI 结构体,聚合所有服务依赖,是 Web 层的核心中枢"},
{49, 170, "页面数据模型 PageData", "统一的页面渲染数据载体,包含设备、任务、告警、配置等所有页面所需数据"},
{171, 222, "辅助视图类型", "DeviceOverviewRow、TaskDeviceRow、ConfigStatusView 等轻量视图模型"},
{223, 545, "构造函数与模板函数注册", "NewUI 创建 UI 实例,注册模板渲染所需的 FuncMapJSON序列化、类型标签、规则映射等"},
{546, 625, "依赖注入方法", "SetStateRepo、SetAuditRepo、SetAlarmCollector 等延迟注入方法"},
{626, 762, "路由注册 Routes", "将 HTTP 路径映射到对应的页面处理函数和 API 动作函数"},
{763, 825, "模板渲染与设备工具方法", "render 模板渲染、findDevice 设备查找、ensureDevicesLoaded 缓存加载"},
{826, 895, "仪表盘页面", "pageDashboard 渲染系统总览:设备统计、告警概览、异常设备、最近任务"},
{896, 1244, "管控台页面", "pageConsole 设备能力矩阵展示、actionConsoleSave 保存管控配置"},
{1245, 1410, "设备列表与批量操作", "pageDevices 列表、actionDeviceAdd 添加、actionDiscoverySearch 发现、actionDevicesBatchAction 批量操作"},
{1410, 1726, "设备详情与单设备控制", "pageDevice 详情、pageDeviceControl 控制、actionDeviceAction 下发动作、actionDeviceConfigApply 配置应用"},
{1727, 1833, "任务管理模块", "pageTasks 列表、taskPageData 数据、actionCreateTask 创建任务、pageTask 详情"},
{1834, 1913, "模型管理", "pageModels 模型概览、actionModelSync 同步模型到设备"},
{1914, 2004, "告警与诊断", "pageDiagnostics 诊断页、pageAlarms 告警中心(分页过滤)"},
{2005, 2066, "资源管理", "pageResources 资源概览、actionResourceSync 资源同步"},
{2067, 2133, "配置资产管理——模板", "pageAssetTemplates 列表、CRUD 操作"},
{2134, 2475, "配置资产管理——模板操作", "actionAssetTemplateCreate 创建、Clone 克隆、Rename 重命名、Delete 删除、GraphSave 可视化保存、Export 导出"},
{2476, 2748, "场景配置与视频通道", "pagePlan/pageAssetProfiles 场景管理、pageRecognitionUnits 视频通道、CRUD"},
{2749, 2856, "通道部署与设备分配", "pageDeviceAssignments 部署矩阵、actionDeviceAssignmentSave/Delete 分配操作"},
{2857, 3038, "视频源与第三方服务", "pageAssetVideoSources 视频源管理、pageAssetIntegrations 集成服务管理、CRUD"},
{3039, 3209, "调试参数管理", "pageAssetOverlays 调试参数列表、actionAssetOverlaySave/Clone/Delete"},
{3210, 3600, "配置预览与格式转换", "assetPageData 跨页共享数据、profileEditor 场景编辑器、configPreview 配置预览渲染"},
{3601, 4414, "设备配置交互 UI", "pageDeviceConfigUI/Friendly 友好配置页面、configPreviewPageData 预览、actionDeviceConfigUIPlan/Apply 应用下发"},
{4415, 4476, "人脸库上传与设备代理", "actionDeviceFaceGalleryUpload 上传人脸图片、actionDeviceFaceGalleryReload 重新加载人脸库"},
{4477, 4652, "视频监控与人脸库管理", "pageMonitor 监控页、proxyHLS 代理推流、actionFaceGalleryAdd/Build 人脸库构建"},
{4653, 4822, "人脸库页面与设备指标 API", "pageFaceGallery 人脸库展示、serveFacePhoto 照片服务、apiDeviceMetrics 指标接口"},
{4823, 4863, "控制台视图模型", "DeviceMetric、ConsoleDeviceData、ConsoleChannel、ConsoleFeature 等控制台用数据结构"},
}
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: go run extract_src.go <source.go>")
os.Exit(1)
}
srcPath := os.Args[1]
f, err := os.Open(srcPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer f.Close()
var lines []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
outPath := "docs/source_code.md"
out, err := os.Create(outPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer out.Close()
w := bufio.NewWriter(out)
// Header
w.WriteString("# 工业安全生产视觉监测平台——核心源代码\n\n")
w.WriteString("**软件名称**:工业安全生产视觉监测平台\n")
w.WriteString("**版本**V1.0\n")
w.WriteString("**开发语言**Go\n")
w.WriteString("**源文件**`internal/web/ui.go`\n")
w.WriteString("**总行数**" + fmt.Sprintf("%d", len(lines)) + "\n\n")
w.WriteString("---\n\n")
w.WriteString("## 模块概览\n\n")
for _, m := range modules {
w.WriteString(fmt.Sprintf("- **%s**(第 %d-%d 行):%s\n", m.Title, m.Start, m.End, m.Comment))
}
w.WriteString("\n---\n\n")
// Find function/type boundaries for better formatting
typeBoundaryRe := regexp.MustCompile(`^(func |type )`)
commentRe := regexp.MustCompile(`^//`) // Sometimes we use // comments
moduleIdx := 0
for i, line := range lines {
lineNum := i + 1
// Check if we need to start a new module section
for moduleIdx < len(modules) && lineNum == modules[moduleIdx].Start {
m := modules[moduleIdx]
w.WriteString(fmt.Sprintf("## 模块 %d%s\n", moduleIdx+1, m.Title))
w.WriteString(fmt.Sprintf("> %s\n\n", m.Comment))
w.WriteString("```go\n")
moduleIdx++
}
// Check if we need to close a module section
if moduleIdx > 0 && lineNum == modules[moduleIdx-1].End+1 {
w.WriteString("```\n\n")
}
// Mark important type/function boundaries (but these are already covered by modules)
_ = typeBoundaryRe
_ = commentRe
w.WriteString(line + "\n")
}
// Close last module if needed
if moduleIdx > 0 && moduleIdx <= len(modules) {
w.WriteString("```\n\n")
}
w.Flush()
fmt.Printf("Done: %s (%d lines)\n", outPath, len(lines))
}

View File

@ -46,8 +46,12 @@ const (
deviceAssignmentPreviewDeviceCount = 8
)
const version = "1.0"
type PageData struct {
Title string
Version string
Year int
ContentHTML template.HTML
Message string
Error string
@ -270,6 +274,20 @@ func NewUI(discovery *service.DiscoveryService, registry *service.RegistryServic
return v
}
},
"ruleLabel": func(v string) string {
switch strings.TrimSpace(v) {
case "unknown_face":
return "陌生人员告警"
case "known_person":
return "已登记人员"
case "non_compliant_workshoe":
return "未穿劳保鞋"
case "non_black_shoe":
return "工鞋不合规"
default:
return v
}
},
"displayDeviceName": func(dev *models.Device, status *ConfigStatusView) string {
if dev == nil {
return "-"
@ -743,6 +761,8 @@ func (u *UI) Routes() (chi.Router, error) {
}
func (u *UI) render(w http.ResponseWriter, r *http.Request, content string, data PageData) {
data.Version = version
data.Year = time.Now().Year()
var buf bytes.Buffer
if err := u.tpl.ExecuteTemplate(&buf, content, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -118,7 +118,7 @@ code,.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.app-shell{display:grid;grid-template-columns:240px minmax(0,1fr);min-height:100vh}
.sidebar{background:var(--sidebar);color:var(--sidebar-text);display:flex;flex-direction:column;padding:18px 14px;position:sticky;top:0;height:100vh}
.brand-block{display:flex;gap:12px;align-items:center;padding:4px 8px 20px}
.brand-block{display:flex;gap:8px;align-items:center;padding:4px 0 20px 4px;margin-left:-8px}
.brand-mark{width:38px;height:38px;border-radius:var(--radius);background:rgba(0,0,0,.28);border:1px solid rgba(255,255,255,.12);display:grid;place-items:center;font-size:13px;font-weight:600;color:var(--primary)}
.brand-title{font-size:14px;font-weight:600}
.side-nav{display:flex;flex-direction:column;gap:4px}
@ -431,3 +431,5 @@ pre{margin-top:12px;padding:12px;border-radius:var(--radius);border:1px solid va
.assignment-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}
.assignment-board-grid{grid-template-columns:1fr}
}
.sidebar-footer{margin-top:auto;padding:16px 8px 0;border-top:1px solid rgba(255,255,255,.08);font-size:11px;color:var(--sidebar-muted);line-height:1.6;text-align:center}

View File

@ -42,7 +42,7 @@
</td>
<td>{{.Channel}}</td>
<td>
<span class="pill warn">{{.RuleName}}</span>
<span class="pill warn">{{ruleLabel .RuleName}}</span>
</td>
<td>{{if .ObjectLabel}}{{.ObjectLabel}}{{else}}-{{end}}</td>
<td>{{if .Confidence}}{{printf "%.2f" .Confidence}}{{else}}-{{end}}</td>

View File

@ -8,7 +8,7 @@
<div style="display:flex;flex-wrap:wrap;gap:8px">
{{range .AlarmRecords}}
<div style="padding:8px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-soft);min-width:200px">
<div style="font-weight:500;font-size:12px;margin-bottom:2px">{{.RuleName}}</div>
<div style="font-weight:500;font-size:12px;margin-bottom:2px">{{ruleLabel .RuleName}}</div>
<div class="muted small mono">{{.Timestamp}}</div>
</div>
{{end}}

View File

@ -15,7 +15,7 @@
<div class="brand-block">
<div class="brand-mark">AI</div>
<div>
<div class="brand-title">视觉识别运维平台</div>
<div class="brand-title">工业安全生产视觉监测平台</div>
</div>
</div>
<nav class="side-nav" aria-label="主导航">
@ -44,6 +44,10 @@
</div>
</details>
</nav>
<div class="sidebar-footer">
<div>北京泰奥理科技有限公司</div>
<div>&copy; {{.Year}} V{{.Version}}</div>
</div>
</aside>
<div class="workspace">
<header class="topbar">