From 858fe61b56d9f05582b3bd3af3d03eb699dffa2f Mon Sep 17 00:00:00 2001 From: Evgeny Demchenko Date: Thu, 4 Jun 2026 09:24:40 +0100 Subject: [PATCH] =?UTF-8?q?Phase=2011b:=20hybrid=20PTZ=20=E2=80=94=20set?= =?UTF-8?q?=5Flayout=20=D1=81=20timed=20motion=20freeze?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User: "PTZ снова не переключает сетки". Причина: при motion-mode set_layout игнорировался. Теперь: применяется + замораживает motion-mode на manual_override_duration_ms_ (60s default). По истечении — auto-возврат в motion-mode. В Composer добавлено: - manual_override_until_ms_ (моноклоk монотонное время) - manual_override_duration_ms_ (default 60s) - set_layout: применяет template, ставит override timestamp - maybe_relayout: пока now < override → пропускаем (sustain manual layout), после → лог "expired, возврат в motion-mode" + force relayout ONVIF server.py одновременно обновлён под актуальные template имена: - PTZ_PRESETS: tpl_1 / tpl_4 / tpl_9 / tpl_16 (вместо single/quad/...) - ContinuousMove zoom-in → tpl_1, zoom-out → tpl_16, pan/tilt → cycle через эти 4 Production smoke: GotoPreset tpl_4 → composer log "manual override 'tpl_4' до +60000ms" PASS. Refs: #195. Co-Authored-By: Claude Opus 4.7 --- include/cuframes_composer/cpp/composer.hpp | 5 +++++ src/cpp/composer.cpp | 25 ++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/include/cuframes_composer/cpp/composer.hpp b/include/cuframes_composer/cpp/composer.hpp index bf16473..f376b76 100644 --- a/include/cuframes_composer/cpp/composer.hpp +++ b/include/cuframes_composer/cpp/composer.hpp @@ -134,6 +134,11 @@ private: std::string committed_signature_; std::string pending_signature_; + /* Manual override (PTZ через set_layout): пока now < manual_override_until_ms_ + * motion-mode "заморожен", композитор держит зафиксированный layout. */ + std::int64_t manual_override_until_ms_ = 0; + int manual_override_duration_ms_ = 60000; + /* Backward-compat overlay list (CLI overlays + detbox). */ std::vector overlays_; diff --git a/src/cpp/composer.cpp b/src/cpp/composer.cpp index 8bf2266..1f731df 100644 --- a/src/cpp/composer.cpp +++ b/src/cpp/composer.cpp @@ -114,11 +114,8 @@ void Composer::set_motion_mode(bool on, int ttl_ms) bool Composer::set_layout(const std::string& name) { - if (motion_mode_) { - std::fprintf(stderr, "[cfc/composer] set_layout('%s') ignored: motion_mode active\n", - name.c_str()); - return false; - } + /* В motion-mode set_layout не игнорируется: применяем + freezing motion + * на manual_override_duration_ms_ (default 60s). После — auto возврат. */ const auto& reg = current_templates(); auto it = std::find_if(reg.begin(), reg.end(), [&](const LayoutTemplate& t) { return t.name == name; }); @@ -131,9 +128,15 @@ bool Composer::set_layout(const std::string& name) pool_.for_each([&](PoolEntry& e) { snap.push_back(&e); }); std::sort(snap.begin(), snap.end(), [](PoolEntry* a, PoolEntry* b) { return a->priority > b->priority; }); + std::int64_t now = now_ms_mono(); layout_.apply(*it, snap, cfg_.width, cfg_.height); committed_signature_ = build_signature(it->name, snap); - committed_at_ms_ = now_ms_mono(); + committed_at_ms_ = now; + if (motion_mode_) { + manual_override_until_ms_ = now + manual_override_duration_ms_; + std::fprintf(stderr, "[cfc/composer] manual override '%s' до +%dms\n", + it->name.c_str(), manual_override_duration_ms_); + } return true; } @@ -207,6 +210,15 @@ void Composer::maybe_relayout() if (!motion_mode_) return; if (current_templates().empty()) return; + /* Manual override freeze. */ + std::int64_t now = now_ms_mono(); + if (manual_override_until_ms_ > now) return; + if (manual_override_until_ms_ != 0) { + std::fprintf(stderr, "[cfc/composer] manual override expired, возврат в motion-mode\n"); + manual_override_until_ms_ = 0; + committed_signature_.clear(); /* форс relayout */ + } + auto active = collect_active(); const LayoutTemplate* tpl = pick_best_fit(static_cast(active.size())); if (!tpl) return; @@ -266,7 +278,6 @@ void Composer::maybe_relayout() if (is_grow && nkeys.size() == ckeys.size()) is_grow = false; /* идентичны */ } - std::int64_t now = now_ms_mono(); if (is_grow) { layout_.apply(*tpl, active, cfg_.width, cfg_.height); committed_signature_ = sig;