refactor: use hardcoded tuning definitions instead of template-embedded tuning section
This commit is contained in:
parent
723239812b
commit
1de96c4c02
@ -21,20 +21,24 @@ type TuningItem struct {
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
// GetTuningItems reads the tuning section from a template and resolves current
|
||||
// values from the template's actual node parameters.
|
||||
// GetTuningItems looks up hardcoded tuning definitions for the template and
|
||||
// resolves current values from the template's actual node parameters.
|
||||
func (s *ConfigPreviewService) GetTuningItems(templateName string) ([]TuningItem, error) {
|
||||
stdName := "std_" + templateName
|
||||
defs := tuningDefinitions[stdName]
|
||||
if len(defs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
raw, _, err := s.readAssetJSON("templates", templateName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items, err := parseTuningItems(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Deep copy defs and resolve current values from template.
|
||||
items := make([]TuningItem, len(defs))
|
||||
copy(items, defs)
|
||||
|
||||
// Resolve current values from the template's actual node config.
|
||||
templateBody, _ := raw["template"].(map[string]any)
|
||||
nodes, _ := templateBody["nodes"].([]any)
|
||||
nodeMap := make(map[string]map[string]any, len(nodes))
|
||||
@ -60,17 +64,20 @@ func (s *ConfigPreviewService) GetTuningItems(templateName string) ([]TuningItem
|
||||
// SaveTuningItems writes tuning values directly into the template's node
|
||||
// parameters and saves the template. Returns affected device IDs.
|
||||
func (s *ConfigPreviewService) SaveTuningItems(templateName string, values map[string]float64) ([]string, error) {
|
||||
stdName := "std_" + templateName
|
||||
defs := tuningDefinitions[stdName]
|
||||
if len(defs) == 0 {
|
||||
return nil, fmt.Errorf("该模板没有可调参数")
|
||||
}
|
||||
|
||||
raw, _, err := s.readAssetJSON("templates", templateName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items, err := parseTuningItems(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply values.
|
||||
// Resolve current values from defs, overriding with form values.
|
||||
items := make([]TuningItem, len(defs))
|
||||
copy(items, defs)
|
||||
for i := range items {
|
||||
if v, ok := values[items[i].Path]; ok {
|
||||
items[i].Value = v
|
||||
@ -167,37 +174,33 @@ func (s *ConfigPreviewService) RedeployDevices(deviceIDs []string) ([]string, er
|
||||
|
||||
// --- path parsing helpers ---
|
||||
|
||||
func parseTuningItems(raw map[string]any) ([]TuningItem, error) {
|
||||
tuningRaw, _ := raw["tuning"].([]any)
|
||||
if len(tuningRaw) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
items := make([]TuningItem, 0, len(tuningRaw))
|
||||
for _, item := range tuningRaw {
|
||||
im, _ := item.(map[string]any)
|
||||
if im == nil {
|
||||
continue
|
||||
}
|
||||
ti := TuningItem{
|
||||
Path: strings.TrimSpace(stringValue(im["path"])),
|
||||
Label: strings.TrimSpace(stringValue(im["label"])),
|
||||
Type: strings.TrimSpace(stringValue(im["type"])),
|
||||
Min: floatValue(im, "min", 0),
|
||||
Max: floatValue(im, "max", 100),
|
||||
Step: floatValue(im, "step", 1),
|
||||
Unit: strings.TrimSpace(stringValue(im["unit"])),
|
||||
Group: strings.TrimSpace(stringValue(im["group"])),
|
||||
}
|
||||
if ti.Type == "" {
|
||||
ti.Type = "slider"
|
||||
}
|
||||
if ti.Path == "" || ti.Label == "" {
|
||||
continue
|
||||
}
|
||||
items = append(items, ti)
|
||||
}
|
||||
return items, nil
|
||||
// tuningDefinitions maps standard template names to their adjustable parameters.
|
||||
// To expose a new parameter: add a TuningItem entry below.
|
||||
var tuningDefinitions = map[string][]TuningItem{
|
||||
"std_workshop_face_recognition_shoe_alarm": {
|
||||
// ── 告警 ──
|
||||
{Path: "alarm_violation.rules[0].cooldown_ms", Label: "劳保鞋告警冷却", Type: "slider", Min: 5000, Max: 300000, Step: 1000, Unit: "ms", Group: "告警"},
|
||||
{Path: "alarm_violation.rules[0].min_duration_ms", Label: "违规最小持续时间", Type: "slider", Min: 200, Max: 10000, Step: 100, Unit: "ms", Group: "告警"},
|
||||
{Path: "alarm_violation.rules[0].min_hits", Label: "违规最小命中帧数", Type: "slider", Min: 1, Max: 10, Step: 1, Unit: "帧", Group: "告警"},
|
||||
{Path: "alarm_violation.rules[0].min_score", Label: "违规最低置信度", Type: "slider", Min: 0.1, Max: 0.9, Step: 0.05, Group: "告警"},
|
||||
// ── 人脸 ──
|
||||
{Path: "alarm_violation.face_rules[0].cooldown_ms", Label: "陌生人脸冷却", Type: "slider", Min: 2000, Max: 120000, Step: 1000, Unit: "ms", Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[0].min_hits", Label: "陌生人脸命中帧数", Type: "slider", Min: 1, Max: 10, Step: 1, Unit: "帧", Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[0].max_known_sim", Label: "陌生人脸相似度上限", Type: "slider", Min: 0.1, Max: 0.8, Step: 0.05, Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[0].hit_window_ms", Label: "陌生人脸命中窗口", Type: "slider", Min: 500, Max: 10000, Step: 500, Unit: "ms", Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[1].cooldown_ms", Label: "已知人员冷却", Type: "slider", Min: 2000, Max: 120000, Step: 1000, Unit: "ms", Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[1].min_sim", Label: "已知人员相似度阈值", Type: "slider", Min: 0.3, Max: 0.95, Step: 0.05, Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[1].min_hits", Label: "已知人员命中帧数", Type: "slider", Min: 1, Max: 10, Step: 1, Unit: "帧", Group: "人脸"},
|
||||
{Path: "alarm_violation.face_rules[1].hit_window_ms", Label: "已知人员命中窗口", Type: "slider", Min: 500, Max: 10000, Step: 500, Unit: "ms", Group: "人脸"},
|
||||
{Path: "recognize_face.threshold.accept", Label: "人脸识别接受阈值", Type: "slider", Min: 0.3, Max: 0.9, Step: 0.05, Group: "人脸"},
|
||||
// ── 工鞋 ──
|
||||
{Path: "detect_shoe.conf", Label: "工鞋检测置信度", Type: "slider", Min: 0.1, Max: 0.8, Step: 0.05, Group: "工鞋"},
|
||||
{Path: "rule_shoe_association.person_shoe_check.min_shoe_score", Label: "工鞋最低分数", Type: "slider", Min: 0.1, Max: 0.6, Step: 0.02, Group: "工鞋"},
|
||||
{Path: "rule_shoe_association.person_shoe_check.min_person_score", Label: "人员最低分数", Type: "slider", Min: 0.1, Max: 0.8, Step: 0.05, Group: "工鞋"},
|
||||
// ── 检测 ──
|
||||
{Path: "detect_person.conf", Label: "人体检测置信度", Type: "slider", Min: 0.1, Max: 0.8, Step: 0.05, Group: "检测"},
|
||||
{Path: "detect_face.conf_thresh", Label: "人脸检测置信度", Type: "slider", Min: 0.2, Max: 0.9, Step: 0.05, Group: "检测"},
|
||||
},
|
||||
}
|
||||
|
||||
func splitTuningPath(fullPath string) (nodeID string, rest string) {
|
||||
|
||||
@ -6,10 +6,7 @@
|
||||
<div>
|
||||
<h2 class="title-with-icon">{{icon "template"}}<span>{{.AssetTemplate.Name}}</span></h2>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a class="btn primary" href="/assets/templates/{{.AssetTemplate.Name}}/settings">{{icon "edit"}} 参数调整</a>
|
||||
<a class="btn secondary" href="/assets/templates">返回模板列表</a>
|
||||
</div>
|
||||
<a class="btn secondary" href="/assets/templates">返回模板列表</a>
|
||||
</div>
|
||||
<div class="info-list">
|
||||
<div><span>模板名</span><strong class="mono">{{.AssetTemplate.Name}}</strong></div>
|
||||
|
||||
@ -65,6 +65,9 @@
|
||||
</div>
|
||||
<div class="actions compact">
|
||||
<a class="btn secondary" href="/assets/templates/{{.AssetTemplate.Name}}/graph{{if .TemplateCloneSource}}?clone_source={{.TemplateCloneSource}}{{end}}">{{icon "assets"}}<span>{{if .AssetTemplate.ReadOnly}}可视化预览{{else if .TemplateCloneSource}}进入可视化编辑{{else}}可视化编辑{{end}}</span></a>
|
||||
{{if not .AssetTemplate.ReadOnly}}
|
||||
<a class="btn secondary" href="/assets/templates/{{.AssetTemplate.Name}}/settings">{{icon "edit"}}<span>参数调整</span></a>
|
||||
{{end}}
|
||||
{{if not .TemplateCloneSource}}
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Binary file not shown.
@ -548,20 +548,5 @@
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"tuning": [
|
||||
{"path":"alarm_violation.rules[0].cooldown_ms","label":"告警冷却时间","type":"slider","min":5000,"max":300000,"step":1000,"unit":"ms","group":"告警"},
|
||||
{"path":"alarm_violation.rules[0].min_duration_ms","label":"最小持续时间","type":"slider","min":200,"max":10000,"step":100,"unit":"ms","group":"告警"},
|
||||
{"path":"alarm_violation.rules[0].min_hits","label":"最小命中帧数","type":"slider","min":1,"max":10,"step":1,"unit":"帧","group":"告警"},
|
||||
{"path":"alarm_violation.rules[0].min_score","label":"最低置信度","type":"slider","min":0.1,"max":0.9,"step":0.05,"group":"告警"},
|
||||
{"path":"alarm_violation.face_rules[0].cooldown_ms","label":"陌生人脸冷却","type":"slider","min":2000,"max":120000,"step":1000,"unit":"ms","group":"人脸"},
|
||||
{"path":"alarm_violation.face_rules[0].min_hits","label":"陌生人脸命中帧数","type":"slider","min":1,"max":10,"step":1,"unit":"帧","group":"人脸"},
|
||||
{"path":"alarm_violation.face_rules[0].max_known_sim","label":"陌生人脸相似度上限","type":"slider","min":0.1,"max":0.8,"step":0.05,"group":"人脸"},
|
||||
{"path":"alarm_violation.face_rules[1].cooldown_ms","label":"已知人员冷却","type":"slider","min":2000,"max":120000,"step":1000,"unit":"ms","group":"人脸"},
|
||||
{"path":"alarm_violation.face_rules[1].min_sim","label":"已知人员相似度阈值","type":"slider","min":0.3,"max":0.95,"step":0.05,"group":"人脸"},
|
||||
{"path":"alarm_violation.face_rules[1].min_hits","label":"已知人员命中帧数","type":"slider","min":1,"max":10,"step":1,"unit":"帧","group":"人脸"},
|
||||
{"path":"recognize_face.threshold.accept","label":"人脸识别阈值","type":"slider","min":0.3,"max":0.9,"step":0.05,"group":"人脸"},
|
||||
{"path":"detect_shoe.conf","label":"工鞋检测阈值","type":"slider","min":0.1,"max":0.8,"step":0.05,"group":"工鞋"},
|
||||
{"path":"detect_person.conf","label":"人体检测阈值","type":"slider","min":0.1,"max":0.8,"step":0.05,"group":"检测"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user