Expose graph node type catalog from agent
This commit is contained in:
parent
45fadc6699
commit
551c6016ff
@ -176,3 +176,48 @@ func TestHandleInfoIncludesCurrentConfigSummary(t *testing.T) {
|
||||
t.Fatalf("overlays = %#v", got["overlays"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleGraphNodeTypesListsRegisteredMediaNodes(t *testing.T) {
|
||||
s := &Server{}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/graph-node-types", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
s.handleGraphNodeTypes(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("status code: got %d body=%s", rr.Code, rr.Body.String())
|
||||
}
|
||||
var got struct {
|
||||
Items []struct {
|
||||
Type string `json:"type"`
|
||||
Label string `json:"label"`
|
||||
Category string `json:"category"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
Defaults map[string]any `json:"defaults"`
|
||||
} `json:"items"`
|
||||
}
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &got); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, item := range got.Items {
|
||||
seen[item.Type] = true
|
||||
if item.Label == "" || item.Category == "" || item.Icon == "" || item.Description == "" {
|
||||
t.Fatalf("node type metadata incomplete: %#v", item)
|
||||
}
|
||||
if item.Defaults["type"] != item.Type {
|
||||
t.Fatalf("defaults should include node type %q, got %#v", item.Type, item.Defaults)
|
||||
}
|
||||
}
|
||||
for _, want := range []string{
|
||||
"input_rtsp", "input_file", "preprocess", "ai_scrfd", "ai_scrfd_sliding",
|
||||
"ai_face_det", "ai_face_recog", "ai_yolo", "ai_shoe_det", "tracker",
|
||||
"logic_gate", "event_fusion", "region_event", "action_recog", "det_post",
|
||||
"osd", "publish", "storage", "alarm", "gate", "zlm_http",
|
||||
} {
|
||||
if !seen[want] {
|
||||
t.Fatalf("missing registered node type %q in %#v", want, got.Items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
167
agent/internal/httpapi/graph_node_types.go
Normal file
167
agent/internal/httpapi/graph_node_types.go
Normal file
@ -0,0 +1,167 @@
|
||||
package httpapi
|
||||
|
||||
import "net/http"
|
||||
|
||||
type graphNodeParam struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Type string `json:"type"`
|
||||
Step string `json:"step,omitempty"`
|
||||
Placeholder string `json:"placeholder,omitempty"`
|
||||
Options []string `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type graphNodeTypeInfo struct {
|
||||
Type string `json:"type"`
|
||||
Label string `json:"label"`
|
||||
Category string `json:"category"`
|
||||
Icon string `json:"icon"`
|
||||
Description string `json:"description"`
|
||||
Defaults map[string]any `json:"defaults"`
|
||||
Params []graphNodeParam `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Server) handleGraphNodeTypes(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
errorJSON(w, http.StatusMethodNotAllowed, "method not allowed")
|
||||
return
|
||||
}
|
||||
if !s.authorize(r, false) {
|
||||
errorJSON(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"items": graphNodeTypesCatalog(),
|
||||
})
|
||||
}
|
||||
|
||||
func graphNodeTypesCatalog() []graphNodeTypeInfo {
|
||||
return []graphNodeTypeInfo{
|
||||
nodeType("input_rtsp", "RTSP 输入", "输入", "camera", "从网络摄像机或流媒体地址读取视频流。", "source", map[string]any{"url": "${rtsp_url}"}, []graphNodeParam{
|
||||
textParam("url", "RTSP 地址", "${rtsp_url}"),
|
||||
numberParam("fps", "输入帧率", "1"),
|
||||
numberParam("width", "宽度", "1"),
|
||||
numberParam("height", "高度", "1"),
|
||||
boolParam("force_tcp", "强制 TCP"),
|
||||
numberParam("reconnect_sec", "重连间隔秒", "1"),
|
||||
}),
|
||||
nodeType("input_file", "文件输入", "输入", "file", "从本地视频文件读取帧,常用于离线验证和回放。", "source", nil, []graphNodeParam{
|
||||
textParam("path", "文件路径", ""),
|
||||
numberParam("fps", "回放帧率", "1"),
|
||||
boolParam("loop", "循环播放"),
|
||||
}),
|
||||
nodeType("preprocess", "图像预处理", "处理", "adjust", "调整尺寸、格式和硬件加速路径,为后续推理或编码准备图像。", "filter", map[string]any{"dst_format": "rgb"}, []graphNodeParam{
|
||||
numberParam("dst_w", "输出宽度", "1"),
|
||||
numberParam("dst_h", "输出高度", "1"),
|
||||
selectParam("dst_format", "输出格式", []string{"rgb", "nv12", "bgr"}),
|
||||
selectParam("resize_mode", "缩放方式", []string{"stretch", "letterbox"}),
|
||||
boolParam("use_rga", "使用 RGA"),
|
||||
textParam("rga_gate", "RGA 通道", ""),
|
||||
}),
|
||||
nodeType("ai_scrfd", "SCRFD 人脸检测", "AI 推理", "scan-face", "使用 SCRFD 模型做人脸检测,适合固定输入尺寸场景。", "filter", nil, faceDetParams()),
|
||||
nodeType("ai_scrfd_sliding", "滑窗人脸检测", "AI 推理", "scan-face", "使用滑窗方式执行 SCRFD 人脸检测,适合高分辨率画面。", "filter", nil, faceDetParams()),
|
||||
nodeType("ai_face_det", "人脸检测", "AI 推理", "face", "通用人脸检测节点,输出人脸框和质量信息。", "filter", nil, faceDetParams()),
|
||||
nodeType("ai_face_recog", "人脸识别", "AI 推理", "face-id", "对检测到的人脸进行特征提取和人脸库匹配。", "filter", nil, []graphNodeParam{
|
||||
textParam("model_path", "模型路径", ""),
|
||||
numberParam("infer_fps", "推理帧率", "0.1"),
|
||||
boolParam("align", "人脸对齐"),
|
||||
boolParam("emit_embedding", "输出特征"),
|
||||
numberParam("max_faces", "最大人脸数", "1"),
|
||||
}),
|
||||
nodeType("ai_yolo", "YOLO 目标检测", "AI 推理", "target", "使用 YOLO 模型检测人员、PPE 或其他目标。", "filter", nil, []graphNodeParam{
|
||||
textParam("model_path", "模型路径", ""),
|
||||
numberParam("infer_fps", "推理帧率", "0.1"),
|
||||
numberParam("model_w", "模型宽度", "1"),
|
||||
numberParam("model_h", "模型高度", "1"),
|
||||
numberParam("conf", "置信度", "0.01"),
|
||||
numberParam("nms", "NMS", "0.01"),
|
||||
}),
|
||||
nodeType("ai_shoe_det", "鞋靴检测", "AI 推理", "shoe", "检测鞋靴和工鞋相关目标,可配合逻辑节点判断违规。", "filter", nil, []graphNodeParam{
|
||||
textParam("model_path", "模型路径", ""),
|
||||
numberParam("infer_fps", "推理帧率", "0.1"),
|
||||
numberParam("conf", "置信度", "0.01"),
|
||||
numberParam("nms", "NMS", "0.01"),
|
||||
boolParam("append_detections", "追加检测结果"),
|
||||
}),
|
||||
nodeType("tracker", "目标跟踪", "处理", "route", "对检测目标分配跟踪 ID,保持跨帧目标状态。", "filter", nil, []graphNodeParam{
|
||||
textParam("mode", "跟踪模式", ""),
|
||||
boolParam("per_class", "按类别跟踪"),
|
||||
numberParam("high_th", "高阈值", "0.01"),
|
||||
numberParam("low_th", "低阈值", "0.01"),
|
||||
numberParam("iou_th", "IOU 阈值", "0.01"),
|
||||
numberParam("max_age_ms", "最大保留毫秒", "1"),
|
||||
}),
|
||||
nodeType("logic_gate", "规则判断", "规则", "branch", "根据检测、跟踪或颜色分析结果进行业务规则判断。", "filter", nil, []graphNodeParam{
|
||||
textParam("mode", "逻辑模式", ""),
|
||||
boolParam("debug", "调试输出"),
|
||||
numberParam("anchor_class", "锚点类别", "1"),
|
||||
numberParam("boots_class", "鞋靴类别", "1"),
|
||||
numberParam("violation_class", "违规类别", "1"),
|
||||
}),
|
||||
nodeType("event_fusion", "事件融合", "规则", "merge", "融合多路事件,减少重复告警并形成更稳定的业务事件。", "filter", nil, nil),
|
||||
nodeType("region_event", "区域事件", "规则", "region", "基于区域、越线或停留规则生成区域行为事件。", "filter", nil, nil),
|
||||
nodeType("action_recog", "行为识别", "AI 推理", "activity", "识别人员行为或动作事件。", "filter", nil, nil),
|
||||
nodeType("det_post", "检测后处理", "处理", "filter", "对检测结果做过滤、映射、合并或类别转换。", "filter", nil, nil),
|
||||
nodeType("osd", "画面叠加", "输出", "overlay", "在视频帧上绘制检测框、文字、人脸识别和事件信息。", "filter", nil, []graphNodeParam{
|
||||
boolParam("draw_bbox", "绘制框"),
|
||||
boolParam("draw_text", "绘制文字"),
|
||||
boolParam("draw_face_det", "绘制人脸检测"),
|
||||
boolParam("draw_face_recog", "绘制人脸识别"),
|
||||
numberParam("line_width", "线宽", "0.1"),
|
||||
numberParam("font_scale", "字体缩放", "0.1"),
|
||||
}),
|
||||
nodeType("publish", "视频输出", "输出", "broadcast", "编码并发布 RTSP、HLS 或其他视频输出。", "sink", nil, []graphNodeParam{
|
||||
selectParam("codec", "编码", []string{"h264", "h265"}),
|
||||
numberParam("fps", "输出帧率", "1"),
|
||||
numberParam("gop", "GOP", "1"),
|
||||
numberParam("bitrate_kbps", "码率 kbps", "1"),
|
||||
boolParam("use_mpp", "使用 MPP"),
|
||||
boolParam("use_ffmpeg_mux", "FFmpeg 封装"),
|
||||
}),
|
||||
nodeType("storage", "本地存储", "输出", "database", "保存帧、事件或中间结果到本地存储。", "sink", nil, nil),
|
||||
nodeType("alarm", "告警动作", "输出", "bell", "根据规则触发日志、抓图、录像片段、外部接口等动作。", "sink", nil, []graphNodeParam{
|
||||
numberParam("eval_fps", "评估帧率", "0.1"),
|
||||
}),
|
||||
nodeType("gate", "流控闸门", "系统", "gate", "控制流程分支或限流,保护下游节点。", "filter", nil, nil),
|
||||
nodeType("zlm_http", "ZLMediaKit HTTP", "系统", "server", "提供 ZLMediaKit 相关 HTTP 文件服务能力。", "sink", nil, nil),
|
||||
}
|
||||
}
|
||||
|
||||
func nodeType(t, label, category, icon, description, role string, defaults map[string]any, params []graphNodeParam) graphNodeTypeInfo {
|
||||
if defaults == nil {
|
||||
defaults = map[string]any{}
|
||||
}
|
||||
defaults["id"] = t
|
||||
defaults["type"] = t
|
||||
defaults["role"] = role
|
||||
defaults["enable"] = true
|
||||
return graphNodeTypeInfo{Type: t, Label: label, Category: category, Icon: icon, Description: description, Defaults: defaults, Params: params}
|
||||
}
|
||||
|
||||
func textParam(key, label, placeholder string) graphNodeParam {
|
||||
return graphNodeParam{Key: key, Label: label, Type: "text", Placeholder: placeholder}
|
||||
}
|
||||
|
||||
func numberParam(key, label, step string) graphNodeParam {
|
||||
return graphNodeParam{Key: key, Label: label, Type: "number", Step: step}
|
||||
}
|
||||
|
||||
func boolParam(key, label string) graphNodeParam {
|
||||
return graphNodeParam{Key: key, Label: label, Type: "boolean"}
|
||||
}
|
||||
|
||||
func selectParam(key, label string, options []string) graphNodeParam {
|
||||
return graphNodeParam{Key: key, Label: label, Type: "select", Options: options}
|
||||
}
|
||||
|
||||
func faceDetParams() []graphNodeParam {
|
||||
return []graphNodeParam{
|
||||
textParam("model_path", "模型路径", ""),
|
||||
numberParam("infer_fps", "推理帧率", "0.1"),
|
||||
numberParam("model_w", "模型宽度", "1"),
|
||||
numberParam("model_h", "模型高度", "1"),
|
||||
numberParam("conf_thresh", "置信度", "0.01"),
|
||||
numberParam("nms_thresh", "NMS", "0.01"),
|
||||
numberParam("max_faces", "最大人脸数", "1"),
|
||||
}
|
||||
}
|
||||
@ -138,6 +138,7 @@ func New(agentCfg config.AgentConfig, baseDir string, ms *mediaserver.Client, st
|
||||
mux.HandleFunc("/v1/media-server/binary", s.handleMediaBinaryUpdate)
|
||||
mux.HandleFunc("/v1/media-server/binary/rollback", s.handleMediaBinaryRollback)
|
||||
mux.HandleFunc("/v1/agent/binary", s.handleAgentBinaryUpdate)
|
||||
mux.HandleFunc("/v1/graph-node-types", s.handleGraphNodeTypes)
|
||||
mux.HandleFunc("/v1/graphs", s.handleGraphs)
|
||||
mux.HandleFunc("/v1/graphs/", s.handleGraphDetail)
|
||||
mux.HandleFunc("/v1/logs/recent", s.handleLogsRecent)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user