From 45fadc669955fb15d7977afdf4905e4d69055e25 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 23 Apr 2026 12:35:33 +0800 Subject: [PATCH] Expose business config metadata in agent --- agent/internal/discovery/discovery.go | 44 +++++++------ agent/internal/discovery/discovery_test.go | 7 ++- agent/internal/httpapi/config_status_test.go | 14 +++-- agent/internal/httpapi/extras.go | 5 ++ agent/internal/httpapi/server.go | 66 ++++++++++---------- configs/profiles/local_3588_test.json | 29 ++++----- tests/test_render_config.py | 6 +- tools/render_config.py | 1 + 8 files changed, 98 insertions(+), 74 deletions(-) diff --git a/agent/internal/discovery/discovery.go b/agent/internal/discovery/discovery.go index b365fa4..0e6877e 100644 --- a/agent/internal/discovery/discovery.go +++ b/agent/internal/discovery/discovery.go @@ -37,26 +37,27 @@ type discoverReq struct { } type discoverReply struct { - Type string `json:"type"` - ReqID string `json:"req_id"` - DeviceID string `json:"device_id"` - DeviceName string `json:"device_name"` - Hostname string `json:"hostname"` - IP string `json:"ip"` - AgentPort int `json:"agent_port"` - MediaPort int `json:"media_port"` - Version string `json:"version"` - BuildID string `json:"build_id"` - BuildType string `json:"build_type"` - GitSHA string `json:"git_sha"` - ConfigID string `json:"config_id,omitempty"` - ConfigVersion string `json:"config_version,omitempty"` - Template string `json:"template,omitempty"` - Profile string `json:"profile,omitempty"` - Overlays []string `json:"overlays,omitempty"` - InstanceName string `json:"instance_name,omitempty"` - InstanceDisplayName string `json:"instance_display_name,omitempty"` - UptimeSec int64 `json:"uptime_sec"` + Type string `json:"type"` + ReqID string `json:"req_id"` + DeviceID string `json:"device_id"` + DeviceName string `json:"device_name"` + Hostname string `json:"hostname"` + IP string `json:"ip"` + AgentPort int `json:"agent_port"` + MediaPort int `json:"media_port"` + Version string `json:"version"` + BuildID string `json:"build_id"` + BuildType string `json:"build_type"` + GitSHA string `json:"git_sha"` + ConfigID string `json:"config_id,omitempty"` + ConfigVersion string `json:"config_version,omitempty"` + BusinessName string `json:"business_name,omitempty"` + Template string `json:"template,omitempty"` + Profile string `json:"profile,omitempty"` + Overlays []string `json:"overlays,omitempty"` + InstanceName string `json:"instance_name,omitempty"` + InstanceDisplayName string `json:"instance_display_name,omitempty"` + UptimeSec int64 `json:"uptime_sec"` } func (r *Responder) Run(ctx context.Context) error { @@ -123,6 +124,7 @@ func (r *Responder) Run(ctx context.Context) error { if summary := readConfigMetadataSummary(r.ConfigPath); summary != nil { reply.ConfigID = summary.ConfigID reply.ConfigVersion = summary.ConfigVersion + reply.BusinessName = summary.BusinessName reply.Template = summary.Template reply.Profile = summary.Profile reply.Overlays = copyStringSlice(summary.Overlays) @@ -151,6 +153,7 @@ func split2lines(s string) (string, string, bool) { type configMetadataSummary struct { ConfigID string ConfigVersion string + BusinessName string Template string Profile string Overlays []string @@ -182,6 +185,7 @@ func metadataSummaryFromMap(metadata map[string]any) *configMetadataSummary { summary := &configMetadataSummary{ ConfigID: stringValue(metadata["config_id"]), ConfigVersion: stringValue(metadata["config_version"]), + BusinessName: stringValue(metadata["business_name"]), Template: stringValue(metadata["template"]), Profile: stringValue(metadata["profile"]), Overlays: stringSliceValue(metadata["overlays"]), diff --git a/agent/internal/discovery/discovery_test.go b/agent/internal/discovery/discovery_test.go index 13d462f..dad5bf3 100644 --- a/agent/internal/discovery/discovery_test.go +++ b/agent/internal/discovery/discovery_test.go @@ -13,7 +13,7 @@ import ( func TestResponderIncludesConfigSummaryInReply(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "media-server.json") - body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["视觉识别终端-A厂区"]},"instances":[]}`) + body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) if err := os.WriteFile(cfgPath, body, 0o644); err != nil { t.Fatalf("write config: %v", err) } @@ -77,7 +77,10 @@ func TestResponderIncludesConfigSummaryInReply(t *testing.T) { if reply["profile"] != "local_3588_test" { t.Fatalf("profile = %#v", reply["profile"]) } - if reply["instance_display_name"] != "视觉识别终端-A厂区" { + if reply["business_name"] != "A厂区视觉识别" { + t.Fatalf("business_name = %#v", reply["business_name"]) + } + if reply["instance_display_name"] != "东门入口" { t.Fatalf("instance_display_name = %#v", reply["instance_display_name"]) } overlays, ok := reply["overlays"].([]any) diff --git a/agent/internal/httpapi/config_status_test.go b/agent/internal/httpapi/config_status_test.go index b000d69..12ffad4 100644 --- a/agent/internal/httpapi/config_status_test.go +++ b/agent/internal/httpapi/config_status_test.go @@ -46,7 +46,7 @@ func (f fakeProcessController) RollbackBinary(string) (procctl.BinaryUpdateResul func TestHandleConfigStatusReportsMetadataHashAndMediaStatus(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") - body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["视觉识别终端-A厂区"]},"instances":[]}`) + body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) if err := os.WriteFile(cfgPath, body, 0o644); err != nil { t.Fatalf("write config: %v", err) } @@ -91,7 +91,10 @@ func TestHandleConfigStatusReportsMetadataHashAndMediaStatus(t *testing.T) { if got["profile"] != "local_3588_test" { t.Fatalf("profile = %#v", got["profile"]) } - if got["instance_display_name"] != "视觉识别终端-A厂区" { + if got["business_name"] != "A厂区视觉识别" { + t.Fatalf("business_name = %#v", got["business_name"]) + } + if got["instance_display_name"] != "东门入口" { t.Fatalf("instance_display_name = %#v", got["instance_display_name"]) } overlays, ok := got["overlays"].([]any) @@ -119,7 +122,7 @@ func TestHandleConfigStatusReportsMetadataHashAndMediaStatus(t *testing.T) { func TestHandleInfoIncludesCurrentConfigSummary(t *testing.T) { dir := t.TempDir() cfgPath := filepath.Join(dir, "config.json") - body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug","production_quiet"],"instance_names":["cam1"],"instance_display_names":["视觉识别终端-A厂区"]},"instances":[]}`) + body := []byte(`{"metadata":{"config_id":"cfg-1","config_version":"v1","business_name":"A厂区视觉识别","template":"workshop_face_shoe_alarm","profile":"local_3588_test","overlays":["face_debug","production_quiet"],"instance_names":["cam1"],"instance_display_names":["东门入口"]},"instances":[]}`) if err := os.WriteFile(cfgPath, body, 0o644); err != nil { t.Fatalf("write config: %v", err) } @@ -162,7 +165,10 @@ func TestHandleInfoIncludesCurrentConfigSummary(t *testing.T) { if got["profile"] != "local_3588_test" { t.Fatalf("profile = %#v", got["profile"]) } - if got["instance_display_name"] != "视觉识别终端-A厂区" { + if got["business_name"] != "A厂区视觉识别" { + t.Fatalf("business_name = %#v", got["business_name"]) + } + if got["instance_display_name"] != "东门入口" { t.Fatalf("instance_display_name = %#v", got["instance_display_name"]) } overlays, ok := got["overlays"].([]any) diff --git a/agent/internal/httpapi/extras.go b/agent/internal/httpapi/extras.go index e394ef9..6e61422 100644 --- a/agent/internal/httpapi/extras.go +++ b/agent/internal/httpapi/extras.go @@ -30,6 +30,7 @@ type configFileStatus struct { type configMetadataSummary struct { ConfigID string ConfigVersion string + BusinessName string Template string Profile string Overlays []string @@ -233,6 +234,9 @@ func (s *Server) configStatusPayload() map[string]any { if summary.ConfigVersion != "" { resp["config_version"] = summary.ConfigVersion } + if summary.BusinessName != "" { + resp["business_name"] = summary.BusinessName + } if summary.Template != "" { resp["template"] = summary.Template } @@ -284,6 +288,7 @@ func metadataSummaryFromMap(metadata map[string]any) *configMetadataSummary { summary := &configMetadataSummary{ ConfigID: stringValue(metadata["config_id"]), ConfigVersion: stringValue(metadata["config_version"]), + BusinessName: stringValue(metadata["business_name"]), Template: stringValue(metadata["template"]), Profile: stringValue(metadata["profile"]), Overlays: stringSliceValue(metadata["overlays"]), diff --git a/agent/internal/httpapi/server.go b/agent/internal/httpapi/server.go index d86cc36..6a30975 100644 --- a/agent/internal/httpapi/server.go +++ b/agent/internal/httpapi/server.go @@ -57,26 +57,27 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } type InfoResponse struct { - DeviceID string `json:"device_id"` - DeviceName string `json:"device_name"` - Hostname string `json:"hostname"` - IP string `json:"ip"` - AgentPort int `json:"agent_port"` - MediaPort int `json:"media_port"` - Version string `json:"version"` - BuildID string `json:"build_id"` - BuildType string `json:"build_type"` - GitSHA string `json:"git_sha"` - ConfigPath string `json:"config_path"` - PreviousConfigPath string `json:"previous_config_path"` - ConfigID string `json:"config_id,omitempty"` - ConfigVersion string `json:"config_version,omitempty"` - Template string `json:"template,omitempty"` - Profile string `json:"profile,omitempty"` - Overlays []string `json:"overlays,omitempty"` - InstanceName string `json:"instance_name,omitempty"` - InstanceDisplayName string `json:"instance_display_name,omitempty"` - UptimeSec int64 `json:"uptime_sec"` + DeviceID string `json:"device_id"` + DeviceName string `json:"device_name"` + Hostname string `json:"hostname"` + IP string `json:"ip"` + AgentPort int `json:"agent_port"` + MediaPort int `json:"media_port"` + Version string `json:"version"` + BuildID string `json:"build_id"` + BuildType string `json:"build_type"` + GitSHA string `json:"git_sha"` + ConfigPath string `json:"config_path"` + PreviousConfigPath string `json:"previous_config_path"` + ConfigID string `json:"config_id,omitempty"` + ConfigVersion string `json:"config_version,omitempty"` + BusinessName string `json:"business_name,omitempty"` + Template string `json:"template,omitempty"` + Profile string `json:"profile,omitempty"` + Overlays []string `json:"overlays,omitempty"` + InstanceName string `json:"instance_name,omitempty"` + InstanceDisplayName string `json:"instance_display_name,omitempty"` + UptimeSec int64 `json:"uptime_sec"` } func New(agentCfg config.AgentConfig, baseDir string, ms *mediaserver.Client, store *modelstore.Store, deviceID string, agentPort int, mediaPort int, version, buildID, buildType, gitSHA string) *Server { @@ -161,23 +162,24 @@ func (s *Server) handleInfo(w http.ResponseWriter, r *http.Request) { ip := sysinfo.PrimaryIPv4() resp := InfoResponse{ - DeviceID: s.deviceID, - DeviceName: s.agentCfg.DeviceName, - Hostname: s.hostname, - IP: ip, - AgentPort: s.agentPort, - MediaPort: s.mediaPort, - Version: s.version, - BuildID: s.buildID, - BuildType: s.buildType, - GitSHA: s.gitSHA, - ConfigPath: s.agentCfg.ConfigPath, + DeviceID: s.deviceID, + DeviceName: s.agentCfg.DeviceName, + Hostname: s.hostname, + IP: ip, + AgentPort: s.agentPort, + MediaPort: s.mediaPort, + Version: s.version, + BuildID: s.buildID, + BuildType: s.buildType, + GitSHA: s.gitSHA, + ConfigPath: s.agentCfg.ConfigPath, PreviousConfigPath: s.agentCfg.ConfigPath + ".last_good.json", - UptimeSec: sysinfo.UptimeSec(), + UptimeSec: sysinfo.UptimeSec(), } if summary := readConfigMetadataSummary(s.agentCfg.ConfigPath); summary != nil { resp.ConfigID = summary.ConfigID resp.ConfigVersion = summary.ConfigVersion + resp.BusinessName = summary.BusinessName resp.Template = summary.Template resp.Profile = summary.Profile resp.Overlays = copyStringSlice(summary.Overlays) diff --git a/configs/profiles/local_3588_test.json b/configs/profiles/local_3588_test.json index f60ebc2..712c062 100644 --- a/configs/profiles/local_3588_test.json +++ b/configs/profiles/local_3588_test.json @@ -1,24 +1,25 @@ { - "name": "local_3588_test", - "description": "Local RK3588 test profile used during workshop face and shoe alarm validation.", - "queue": { - "size": 8, - "strategy": "drop_oldest" - }, + "description": "用于B厂的人脸识别和劳保鞋检测", + "business_name": "B厂区视觉识别", "instances": [ { "name": "cam1", - "template": "workshop_face_shoe_alarm", "params": { - "display_name": "视觉识别终端-A厂区", + "channel_no": "cam1", "device_code": "rk3588-a-001", - "site_name": "A厂区", - "rtsp_url": "rtsp://10.0.0.49:8554/cam", + "display_name": "B厂区通道1", "publish_hls_path": "./web/hls/cam1/index.m3u8", - "publish_rtsp_port": 8555, "publish_rtsp_path": "/live/cam1", - "channel_no": "cam1" - } + "publish_rtsp_port": 8555, + "rtsp_url": "rtsp://10.0.0.49:8554/cam", + "site_name": "B厂区" + }, + "template": "workshop_face_shoe_alarm" } - ] + ], + "name": "local_3588_test", + "queue": { + "size": 8, + "strategy": "drop_oldest" + } } diff --git a/tests/test_render_config.py b/tests/test_render_config.py index bf0f5bd..1272b56 100644 --- a/tests/test_render_config.py +++ b/tests/test_render_config.py @@ -305,11 +305,12 @@ class RenderConfigTest(unittest.TestCase): """ { "name": "local_3588_test", + "business_name": "A厂区视觉识别", "instances": [ { "name": "cam1", "params": { - "display_name": "视觉识别终端-A厂区", + "display_name": "东门入口", "rtsp_url": "rtsp://example/cam1" } } @@ -327,8 +328,9 @@ class RenderConfigTest(unittest.TestCase): ) self.assertEqual(rendered["metadata"]["profile"], "local_3588_test") + self.assertEqual(rendered["metadata"]["business_name"], "A厂区视觉识别") self.assertEqual(rendered["metadata"]["instance_names"], ["cam1"]) - self.assertEqual(rendered["metadata"]["instance_display_names"], ["视觉识别终端-A厂区"]) + self.assertEqual(rendered["metadata"]["instance_display_names"], ["东门入口"]) if __name__ == "__main__": diff --git a/tools/render_config.py b/tools/render_config.py index 9f7fa7e..e2b5ae4 100644 --- a/tools/render_config.py +++ b/tools/render_config.py @@ -189,6 +189,7 @@ def render( "template": tpl_name, "template_path": template_path.as_posix(), "profile": profile_name, + "business_name": str(profile.get("business_name") or "").strip(), "profile_path": profile_path.as_posix(), "instance_names": instance_names, "instance_display_names": instance_display_names,