修复bugagent
This commit is contained in:
parent
d83f2eb749
commit
ce8ae2b448
@ -71,6 +71,28 @@ func (s *Server) handleConfigUIState(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
root, _ := readUIRootConfig(s.agentCfg.ConfigPath)
|
||||
|
||||
mode := "instances"
|
||||
warnings := []string{}
|
||||
if st, err := os.Stat(s.agentCfg.ConfigPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
warnings = append(warnings, "config file not found: "+s.agentCfg.ConfigPath)
|
||||
} else {
|
||||
warnings = append(warnings, "config stat failed: "+err.Error())
|
||||
}
|
||||
} else if st.IsDir() {
|
||||
warnings = append(warnings, "config path is a directory: "+s.agentCfg.ConfigPath)
|
||||
}
|
||||
instances := root.Instances
|
||||
// If current config is graphs-only, do a best-effort import so GUI can edit.
|
||||
if len(instances) == 0 && len(root.Graphs) > 0 {
|
||||
mode = "graphs_imported"
|
||||
inst, warn := inferInstancesFromGraphs(root.Graphs)
|
||||
if len(inst) > 0 {
|
||||
instances = inst
|
||||
}
|
||||
warnings = append(warnings, warn...)
|
||||
}
|
||||
|
||||
// Force templates/instances-only view.
|
||||
// templates = union(current templates, built-in templates)
|
||||
tplKeys := map[string]bool{}
|
||||
@ -90,12 +112,412 @@ func (s *Server) handleConfigUIState(w http.ResponseWriter, r *http.Request) {
|
||||
"ok": true,
|
||||
"global": root.Global,
|
||||
"queue": root.Queue,
|
||||
"instances": root.Instances,
|
||||
"instances": instances,
|
||||
"templates": mergedTpls,
|
||||
"mode": mode,
|
||||
"warnings": warnings,
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func inferInstancesFromGraphs(graphs []map[string]any) ([]UIInstance, []string) {
|
||||
instances := []UIInstance{}
|
||||
warnings := []string{}
|
||||
used := map[string]int{}
|
||||
|
||||
for _, g := range graphs {
|
||||
gname := asString(g["name"])
|
||||
nodes := asSlice(g["nodes"])
|
||||
if len(nodes) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Try templates in priority order.
|
||||
if inst, ok := inferFaceDetRecog(gname, nodes); ok {
|
||||
inst.Name = uniquify(inst.Name, used)
|
||||
instances = append(instances, inst)
|
||||
continue
|
||||
}
|
||||
if inst, ok := inferFaceDet(gname, nodes); ok {
|
||||
inst.Name = uniquify(inst.Name, used)
|
||||
instances = append(instances, inst)
|
||||
continue
|
||||
}
|
||||
if inst, ok := inferYoloAlarmMinio(gname, nodes); ok {
|
||||
inst.Name = uniquify(inst.Name, used)
|
||||
instances = append(instances, inst)
|
||||
continue
|
||||
}
|
||||
if inst, ok := inferYolo(gname, nodes); ok {
|
||||
inst.Name = uniquify(inst.Name, used)
|
||||
instances = append(instances, inst)
|
||||
continue
|
||||
}
|
||||
if inst, ok := inferTranscode(gname, nodes); ok {
|
||||
inst.Name = uniquify(inst.Name, used)
|
||||
instances = append(instances, inst)
|
||||
continue
|
||||
}
|
||||
|
||||
if gname == "" {
|
||||
gname = "noname"
|
||||
}
|
||||
warnings = append(warnings, "graph not recognized for templates/instances import: "+gname)
|
||||
}
|
||||
|
||||
return instances, warnings
|
||||
}
|
||||
|
||||
func inferTranscode(graphName string, nodes []any) (UIInstance, bool) {
|
||||
in := findNodeByType(nodes, "input_rtsp")
|
||||
pub := findNodeByType(nodes, "publish")
|
||||
if in == nil || pub == nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
if findNodeByType(nodes, "ai_yolo") != nil || findNodeByType(nodes, "ai_face_det") != nil || findNodeByType(nodes, "ai_face_recog") != nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
name := deriveNameFromPublish(pub, graphName)
|
||||
params := map[string]any{}
|
||||
params["name"] = name
|
||||
if url := asString(in["url"]); url != "" {
|
||||
params["url"] = url
|
||||
} else {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
params["fps"] = asIntOr(in["fps"], asIntOr(pub["fps"], 25))
|
||||
params["src_w"] = asIntOr(in["width"], 1920)
|
||||
params["src_h"] = asIntOr(in["height"], 1080)
|
||||
params["gop"] = asIntOr(pub["gop"], 50)
|
||||
params["bitrate_kbps"] = asIntOr(pub["bitrate_kbps"], 2000)
|
||||
applyPublishOutputsToParams(pub, params)
|
||||
return UIInstance{Name: name, Template: "transcode_rtsp_hls", Params: params}, true
|
||||
}
|
||||
|
||||
func inferYolo(graphName string, nodes []any) (UIInstance, bool) {
|
||||
in := findNodeByType(nodes, "input_rtsp")
|
||||
ai := findNodeByType(nodes, "ai_yolo")
|
||||
pub := findNodeByType(nodes, "publish")
|
||||
if in == nil || ai == nil || pub == nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
if findNodeByType(nodes, "alarm") != nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
name := deriveNameFromPublish(pub, graphName)
|
||||
params := map[string]any{}
|
||||
params["name"] = name
|
||||
url := asString(in["url"])
|
||||
model := asString(ai["model_path"])
|
||||
if url == "" || model == "" {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
params["url"] = url
|
||||
params["model_path"] = model
|
||||
params["fps"] = asIntOr(in["fps"], asIntOr(pub["fps"], 25))
|
||||
params["src_w"] = asIntOr(in["width"], 1920)
|
||||
params["src_h"] = asIntOr(in["height"], 1080)
|
||||
params["gop"] = asIntOr(pub["gop"], 50)
|
||||
params["bitrate_kbps"] = asIntOr(pub["bitrate_kbps"], 2000)
|
||||
applyPublishOutputsToParams(pub, params)
|
||||
return UIInstance{Name: name, Template: "yolo_rtsp_hls", Params: params}, true
|
||||
}
|
||||
|
||||
func inferYoloAlarmMinio(graphName string, nodes []any) (UIInstance, bool) {
|
||||
in := findNodeByType(nodes, "input_rtsp")
|
||||
ai := findNodeByType(nodes, "ai_yolo")
|
||||
alarm := findNodeByType(nodes, "alarm")
|
||||
pub := findNodeByType(nodes, "publish")
|
||||
if in == nil || ai == nil || alarm == nil || pub == nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
name := deriveNameFromPublish(pub, graphName)
|
||||
params := map[string]any{}
|
||||
params["name"] = name
|
||||
url := asString(in["url"])
|
||||
model := asString(ai["model_path"])
|
||||
if url == "" || model == "" {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
params["url"] = url
|
||||
params["model_path"] = model
|
||||
params["fps"] = asIntOr(in["fps"], asIntOr(pub["fps"], 25))
|
||||
params["src_w"] = asIntOr(in["width"], 1920)
|
||||
params["src_h"] = asIntOr(in["height"], 1080)
|
||||
params["gop"] = asIntOr(pub["gop"], 50)
|
||||
params["bitrate_kbps"] = asIntOr(pub["bitrate_kbps"], 2000)
|
||||
applyPublishOutputsToParams(pub, params)
|
||||
|
||||
// cooldown
|
||||
if rules := asSlice(alarm["rules"]); len(rules) > 0 {
|
||||
if r0 := asMap(rules[0]); r0 != nil {
|
||||
params["cooldown_ms"] = asIntOr(r0["cooldown_ms"], 3000)
|
||||
}
|
||||
}
|
||||
|
||||
// minio from snapshot/clip upload
|
||||
actions := asMap(alarm["actions"])
|
||||
upl := firstMinioUpload(actions)
|
||||
if upl == nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
ep := asString(upl["endpoint"])
|
||||
bucket := asString(upl["bucket"])
|
||||
ak := asString(upl["access_key"])
|
||||
sk := asString(upl["secret_key"])
|
||||
if ep == "" || bucket == "" || ak == "" || sk == "" {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
params["minio_endpoint"] = ep
|
||||
params["minio_bucket"] = bucket
|
||||
params["minio_ak"] = ak
|
||||
params["minio_sk"] = sk
|
||||
if region := asString(upl["region"]); region != "" {
|
||||
params["minio_region"] = region
|
||||
} else {
|
||||
params["minio_region"] = "us-east-1"
|
||||
}
|
||||
|
||||
return UIInstance{Name: name, Template: "yolo_alarm_minio", Params: params}, true
|
||||
}
|
||||
|
||||
func inferFaceDet(graphName string, nodes []any) (UIInstance, bool) {
|
||||
in := findNodeByType(nodes, "input_rtsp")
|
||||
det := findNodeByType(nodes, "ai_face_det")
|
||||
pub := findNodeByType(nodes, "publish")
|
||||
if in == nil || det == nil || pub == nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
if findNodeByType(nodes, "ai_face_recog") != nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
name := deriveNameFromPublish(pub, graphName)
|
||||
params := map[string]any{}
|
||||
params["name"] = name
|
||||
url := asString(in["url"])
|
||||
model := asString(det["model_path"])
|
||||
if url == "" || model == "" {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
params["url"] = url
|
||||
params["det_model_path"] = model
|
||||
params["fps"] = asIntOr(in["fps"], asIntOr(pub["fps"], 25))
|
||||
params["src_w"] = asIntOr(in["width"], 1920)
|
||||
params["src_h"] = asIntOr(in["height"], 1080)
|
||||
params["gop"] = asIntOr(pub["gop"], 50)
|
||||
params["bitrate_kbps"] = asIntOr(pub["bitrate_kbps"], 2000)
|
||||
applyPublishOutputsToParams(pub, params)
|
||||
return UIInstance{Name: name, Template: "face_det_rtsp_hls", Params: params}, true
|
||||
}
|
||||
|
||||
func inferFaceDetRecog(graphName string, nodes []any) (UIInstance, bool) {
|
||||
in := findNodeByType(nodes, "input_rtsp")
|
||||
det := findNodeByType(nodes, "ai_face_det")
|
||||
rec := findNodeByType(nodes, "ai_face_recog")
|
||||
pub := findNodeByType(nodes, "publish")
|
||||
if in == nil || det == nil || rec == nil || pub == nil {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
name := deriveNameFromPublish(pub, graphName)
|
||||
params := map[string]any{}
|
||||
params["name"] = name
|
||||
url := asString(in["url"])
|
||||
detModel := asString(det["model_path"])
|
||||
recModel := asString(rec["model_path"])
|
||||
if url == "" || detModel == "" || recModel == "" {
|
||||
return UIInstance{}, false
|
||||
}
|
||||
params["url"] = url
|
||||
params["det_model_path"] = detModel
|
||||
params["recog_model_path"] = recModel
|
||||
params["fps"] = asIntOr(in["fps"], asIntOr(pub["fps"], 25))
|
||||
params["src_w"] = asIntOr(in["width"], 1920)
|
||||
params["src_h"] = asIntOr(in["height"], 1080)
|
||||
params["gop"] = asIntOr(pub["gop"], 50)
|
||||
params["bitrate_kbps"] = asIntOr(pub["bitrate_kbps"], 2000)
|
||||
applyPublishOutputsToParams(pub, params)
|
||||
|
||||
if th := asMap(rec["threshold"]); th != nil {
|
||||
params["thr_accept"] = asFloatOr(th["accept"], 0.45)
|
||||
params["thr_margin"] = asFloatOr(th["margin"], 0.05)
|
||||
} else {
|
||||
params["thr_accept"] = 0.45
|
||||
params["thr_margin"] = 0.05
|
||||
}
|
||||
if gal := asMap(rec["gallery"]); gal != nil {
|
||||
if p := asString(gal["path"]); p != "" {
|
||||
params["gallery_path"] = p
|
||||
} else {
|
||||
params["gallery_path"] = "./models/face_gallery.db"
|
||||
}
|
||||
} else {
|
||||
params["gallery_path"] = "./models/face_gallery.db"
|
||||
}
|
||||
|
||||
return UIInstance{Name: name, Template: "face_det_recog_rtsp_hls", Params: params}, true
|
||||
}
|
||||
|
||||
func findNodeByType(nodes []any, typ string) map[string]any {
|
||||
for _, n := range nodes {
|
||||
m := asMap(n)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
if asString(m["type"]) == typ {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deriveNameFromPublish(pub map[string]any, fallback string) string {
|
||||
outs := asSlice(pub["outputs"])
|
||||
for _, o := range outs {
|
||||
om := asMap(o)
|
||||
if om == nil {
|
||||
continue
|
||||
}
|
||||
if asString(om["proto"]) != "rtsp_server" {
|
||||
continue
|
||||
}
|
||||
p := asString(om["path"])
|
||||
if strings.HasPrefix(p, "/live/") {
|
||||
n := strings.TrimPrefix(p, "/live/")
|
||||
n = strings.Trim(n, "/")
|
||||
if n != "" {
|
||||
return n
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(fallback) != "" {
|
||||
return fallback
|
||||
}
|
||||
return "cam"
|
||||
}
|
||||
|
||||
func applyPublishOutputsToParams(pub map[string]any, params map[string]any) {
|
||||
outs := asSlice(pub["outputs"])
|
||||
for _, o := range outs {
|
||||
om := asMap(o)
|
||||
if om == nil {
|
||||
continue
|
||||
}
|
||||
proto := asString(om["proto"])
|
||||
if proto == "rtsp_server" {
|
||||
params["rtsp_port"] = asIntOr(om["port"], asIntOr(params["rtsp_port"], 8555))
|
||||
}
|
||||
if proto == "hls" {
|
||||
if p := asString(om["path"]); p != "" {
|
||||
params["hls_path"] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func firstMinioUpload(actions map[string]any) map[string]any {
|
||||
if actions == nil {
|
||||
return nil
|
||||
}
|
||||
// Prefer snapshot.upload.
|
||||
if snap := asMap(actions["snapshot"]); snap != nil {
|
||||
if up := asMap(snap["upload"]); up != nil {
|
||||
if asString(up["type"]) == "minio" {
|
||||
return up
|
||||
}
|
||||
}
|
||||
}
|
||||
if clip := asMap(actions["clip"]); clip != nil {
|
||||
if up := asMap(clip["upload"]); up != nil {
|
||||
if asString(up["type"]) == "minio" {
|
||||
return up
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func uniquify(name string, used map[string]int) string {
|
||||
base := name
|
||||
if base == "" {
|
||||
base = "cam"
|
||||
}
|
||||
if used[base] == 0 {
|
||||
used[base] = 1
|
||||
return base
|
||||
}
|
||||
used[base]++
|
||||
return fmt.Sprintf("%s_%d", base, used[base])
|
||||
}
|
||||
|
||||
func asMap(v any) map[string]any {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if m, ok := v.(map[string]any); ok {
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func asSlice(v any) []any {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if s, ok := v.([]any); ok {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func asString(v any) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
if s, ok := v.(string); ok {
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func asIntOr(v any, def int) int {
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
switch x := v.(type) {
|
||||
case float64:
|
||||
return int(x)
|
||||
case int:
|
||||
return x
|
||||
case int32:
|
||||
return int(x)
|
||||
case int64:
|
||||
return int(x)
|
||||
case string:
|
||||
// ignore parse errors
|
||||
return def
|
||||
default:
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
func asFloatOr(v any, def float64) float64 {
|
||||
if v == nil {
|
||||
return def
|
||||
}
|
||||
switch x := v.(type) {
|
||||
case float64:
|
||||
return x
|
||||
case int:
|
||||
return float64(x)
|
||||
case int32:
|
||||
return float64(x)
|
||||
case int64:
|
||||
return float64(x)
|
||||
default:
|
||||
return def
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleConfigUIPlan(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
errorJSON(w, http.StatusMethodNotAllowed, "method not allowed")
|
||||
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user