From cd6d299bd3f4a3e445fa61e6b1a8bdb78c3fc44b Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 20 Apr 2026 12:27:39 +0800 Subject: [PATCH] Move shared config params into template --- configs/profiles/local_3588_test.json | 9 +-- .../templates/workshop_face_shoe_alarm.json | 9 +++ tests/test_render_config.py | 56 ++++++++++++++++++- tools/render_config.py | 27 ++++++++- 4 files changed, 91 insertions(+), 10 deletions(-) diff --git a/configs/profiles/local_3588_test.json b/configs/profiles/local_3588_test.json index 2f91eb4..f60ebc2 100644 --- a/configs/profiles/local_3588_test.json +++ b/configs/profiles/local_3588_test.json @@ -17,14 +17,7 @@ "publish_hls_path": "./web/hls/cam1/index.m3u8", "publish_rtsp_port": 8555, "publish_rtsp_path": "/live/cam1", - "channel_no": "cam1", - "minio_endpoint": "http://10.0.0.49:9000", - "minio_bucket": "myminio", - "minio_access_key": "admin", - "minio_secret_key": "password", - "external_get_token_url": "http://10.0.0.49:8080/api/getToken", - "external_put_message_url": "http://10.0.0.49:8080/api/putMessage", - "tenant_code": "32" + "channel_no": "cam1" } } ] diff --git a/configs/templates/workshop_face_shoe_alarm.json b/configs/templates/workshop_face_shoe_alarm.json index 1e760cc..11ae70f 100644 --- a/configs/templates/workshop_face_shoe_alarm.json +++ b/configs/templates/workshop_face_shoe_alarm.json @@ -2,6 +2,15 @@ "name": "workshop_face_shoe_alarm", "description": "Full 1080p workshop pipeline with face detection/recognition, person tracking, shoe detection, OSD, publishing, alarms, snapshots, clips, and external API upload.", "source": "configs/full_pipeline_1080p_test_alarm.json", + "params": { + "minio_endpoint": "http://10.0.0.49:9000", + "minio_bucket": "myminio", + "minio_access_key": "admin", + "minio_secret_key": "password", + "external_get_token_url": "http://10.0.0.49:8080/api/getToken", + "external_put_message_url": "http://10.0.0.49:8080/api/putMessage", + "tenant_code": "32" + }, "template": { "executor": { "batch_size": 2, diff --git a/tests/test_render_config.py b/tests/test_render_config.py index fc04c04..bf0f5bd 100644 --- a/tests/test_render_config.py +++ b/tests/test_render_config.py @@ -39,6 +39,14 @@ class RenderConfigTest(unittest.TestCase): self.assertIn("publish_rtsp_port", params) self.assertIn("publish_rtsp_path", params) self.assertIn("channel_no", params) + self.assertNotIn("minio_endpoint", params) + self.assertNotIn("external_get_token_url", params) + self.assertNotIn("tenant_code", params) + + shared_params = template["params"] + self.assertEqual(shared_params["minio_endpoint"], "http://10.0.0.49:9000") + self.assertEqual(shared_params["external_get_token_url"], "http://10.0.0.49:8080/api/getToken") + self.assertEqual(shared_params["tenant_code"], "32") def test_render_profile_no_longer_requires_face_gallery_path(self): profile_path = REPO_ROOT / "configs" / "profiles" / "local_3588_test.json" @@ -62,6 +70,7 @@ class RenderConfigTest(unittest.TestCase): module = load_module() template = { "name": "pipeline", + "params": {"tenant_code": "32"}, "template": { "executor": {"batch_size": 2}, "nodes": [ @@ -94,7 +103,7 @@ class RenderConfigTest(unittest.TestCase): root = { "templates": {module.template_name(template, pathlib.Path("x.json")): module.template_body(template)}, - "instances": module.profile_instances(profile, "pipeline"), + "instances": module.merge_template_params(module.profile_instances(profile, "pipeline"), module.template_params(template)), "queue": profile["queue"], } rendered = module.apply_overlay(root, overlay) @@ -102,10 +111,55 @@ class RenderConfigTest(unittest.TestCase): self.assertEqual(rendered["queue"]["size"], 8) self.assertEqual(rendered["instances"][0]["template"], "pipeline") self.assertEqual(rendered["instances"][0]["params"]["rtsp_url"], "rtsp://example/cam1") + self.assertEqual(rendered["instances"][0]["params"]["tenant_code"], "32") node_override = rendered["instances"][0]["override"]["nodes"]["face_recog"] self.assertTrue(node_override["debug"]["enabled"]) self.assertTrue(node_override["debug"]["log_matches"]) + def test_template_params_fill_placeholders_without_staying_in_profile(self): + module = load_module() + with tempfile.TemporaryDirectory() as tmp_dir: + tmp = pathlib.Path(tmp_dir) + template_path = tmp / "template.json" + profile_path = tmp / "profile.json" + + template_path.write_text( + """ + { + "name": "pipeline", + "params": { + "minio_endpoint": "http://10.0.0.49:9000", + "tenant_code": "32" + }, + "template": { + "nodes": [{"id": "alarm", "type": "alarm", "endpoint": "${minio_endpoint}", "tenant": "${tenant_code}"}], + "edges": [] + } + } + """, + encoding="utf-8", + ) + profile_path.write_text( + """ + { + "name": "local_3588_test", + "instances": [ + { + "name": "cam1", + "params": {"rtsp_url": "rtsp://example/cam1"} + } + ] + } + """, + encoding="utf-8", + ) + + rendered = module.render(template_path, profile_path, []) + params = rendered["instances"][0]["params"] + self.assertEqual(params["minio_endpoint"], "http://10.0.0.49:9000") + self.assertEqual(params["tenant_code"], "32") + self.assertEqual(params["rtsp_url"], "rtsp://example/cam1") + def test_render_applies_multiple_overlays_in_order(self): module = load_module() with tempfile.TemporaryDirectory() as tmp_dir: diff --git a/tools/render_config.py b/tools/render_config.py index a9f925f..9f7fa7e 100644 --- a/tools/render_config.py +++ b/tools/render_config.py @@ -50,6 +50,15 @@ def template_body(template_doc: JsonObject) -> JsonObject: return {key: copy.deepcopy(value) for key, value in body.items() if key in allowed} +def template_params(template_doc: JsonObject) -> JsonObject: + params = template_doc.get("params", {}) + if params is None: + return {} + if not isinstance(params, dict): + raise ValueError("template params must be a JSON object") + return copy.deepcopy(params) + + def profile_instances(profile: JsonObject, tpl_name: str) -> list[JsonObject]: if "instances" in profile: instances = profile["instances"] @@ -77,6 +86,22 @@ def profile_instances(profile: JsonObject, tpl_name: str) -> list[JsonObject]: ] +def merge_template_params(instances: list[JsonObject], shared_params: JsonObject) -> list[JsonObject]: + if not shared_params: + return instances + out: list[JsonObject] = [] + for item in instances: + inst = copy.deepcopy(item) + params = inst.get("params", {}) + if params is None: + params = {} + if not isinstance(params, dict): + raise ValueError("instance params must be a JSON object") + inst["params"] = deep_merge(shared_params, params) + out.append(inst) + return out + + def merge_instance_patch(instance: JsonObject, patch: JsonObject) -> JsonObject: merged = copy.deepcopy(instance) if "params" in patch: @@ -137,7 +162,7 @@ def render( root: JsonObject = { "templates": {tpl_name: template_body(template_doc)}, - "instances": profile_instances(profile, tpl_name), + "instances": merge_template_params(profile_instances(profile, tpl_name), template_params(template_doc)), } for key in ("global", "queue"): if key in profile: