refactor: use hardcoded tuning definitions instead of template-embedded tuning section

This commit is contained in:
tian 2026-07-27 19:07:00 +08:00
parent 723239812b
commit 1de96c4c02
5 changed files with 52 additions and 64 deletions

View File

@ -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) {

View File

@ -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>

View File

@ -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.

View File

@ -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":"检测"}
]
}
}