Files
Aethera/mcp/core/Control_Service.ipp
2026-09-04 21:55:18 +08:00

88 lines
5.2 KiB
C++

#pragma once
#include "runtime/Gallery_Runtime.hpp"
#include <frame_policy/src/Throttled_Latest_Only.hpp>
#include <array>
#include <atomic>
#include <optional>
#include <unordered_map>
namespace aethera::mcp {
struct Control_Service::Private {
struct Entry;
struct Entry_Retirement;
struct Entry_Stop_Lifetime;
struct Policy_Scene : Def<Policy_Scene, Root> {
struct Private;
};
struct Headless_Sink : Def<Headless_Sink, Root> {
struct Private;
};
struct Entry {
Entry(std::string id, web::Gallery_Build build, proxy<frame_policy::Sink> sink, std::uint32_t width, std::uint32_t height);
~Entry();
Entry(const Entry&) = delete;
Entry& operator=(const Entry&) = delete;
void start();
void stop(std::shared_ptr<Entry_Retirement> retirement) noexcept;
void request_frame(web::Gallery_Frame_Request request);
proxy<scene::Frame> create_frame();
scene::Render_Result render(proxy<scene::Frame>& frame, scene::Render_Completion completion);
[[nodiscard]] nlohmann::json schema() const;
[[nodiscard]] nlohmann::json diagnostics() const;
void reset_diagnostics() noexcept;
void request_trace(std::size_t frame_count);
[[nodiscard]] nlohmann::json trace() const;
[[nodiscard]] std::shared_ptr<Throttled_Latest_only> current_policy() const noexcept;
[[nodiscard]] bool reserve_trace() noexcept;
void restore_trace() noexcept;
void store_trace(const Taskflow_Execution_Trace& trace, const web::Gallery_Frame_Request& request, const nlohmann::json& backend = {});
std::unique_ptr<web::Gallery_Component> components; /* Sole owner of plot models, camera and axes; declared before Scene so it outlives every Scene borrow. */
web::Gallery_Scene scene; /* Sole owner of the dimension-specific Scene. */
proxy<frame_policy::Sink> sink; /* Sole owner until start transfers the concrete media Sink into Frame Policy. */
std::atomic<std::shared_ptr<Throttled_Latest_only>> policy{}; /* Shared only across asynchronous stop retirement. */
std::string id; /* Stable catalog identifier. */
std::uint32_t output_width{}; /* Default width for timer-driven requests. */
std::uint32_t output_height{}; /* Default height for timer-driven requests. */
std::chrono::steady_clock::time_point started_at{std::chrono::steady_clock::now()}; /* Runtime animation clock origin. */
std::atomic_uint64_t next_sequence{}; /* Authoritative next rendered-frame sequence source. */
std::atomic<std::shared_ptr<const web::Gallery_Frame_Request>> requested_frame{}; /* Latest explicit request consumed by one subsequent policy tick. */
std::atomic<std::shared_ptr<const nlohmann::json>> datoviz_observation{}; /* Latest immutable 3D backend diagnostic publication. */
static constexpr std::size_t maximum_taskflow_trace_frames{120};
std::atomic_size_t taskflow_trace_remaining{}; /* Number of requested successful Scene executions not yet captured. */
std::atomic_uint64_t taskflow_trace_control{}; /* High 32 bits requested, low 32 bits captured. */
std::array<std::atomic<std::shared_ptr<const nlohmann::json>>, maximum_taskflow_trace_frames> taskflow_trace_slots{}; /* Immutable captured frame traces. */
};
struct Entry_Retirement {
std::shared_ptr<Entry> entry; /* Sole retirement owner after the registry has detached the Entry. */
std::function<void()> completion;
~Entry_Retirement() noexcept;
};
struct Entry_Stop_Lifetime {
std::shared_ptr<Entry_Retirement> retirement; /* Declared before policy so Entry retirement is released after the stopped policy. */
std::shared_ptr<Throttled_Latest_only> policy;
};
std::unordered_map<std::string, std::shared_ptr<Entry>> plots; /* Stable plot registry; entries retire themselves asynchronously. */
bool stop_started{}; /* Sole Control_Service lifecycle authority; public operations are unavailable after the one shutdown begins. */
};
struct Control_Service::Private::Policy_Scene::Private : Prev_Private {
explicit Private(Entry* entry) : entry(entry) {}
proxy<scene::Frame> create_frame();
scene::Render_Result render(proxy<scene::Frame>& frame, scene::Render_Completion completion);
Entry* entry; /* Required non-owning borrow; Entry owns the policy and outlives asynchronous stop completion. */
};
struct Control_Service::Private::Headless_Sink::Private : Prev_Private {
void send(proxy<scene::Frame>& frame, frame_policy::Frame_Completion completion) { completion(frame); }
};
} // namespace aethera::mcp