优化
This commit is contained in:
@@ -42,7 +42,7 @@ public:
|
||||
nlohmann::json schema() const override {
|
||||
nlohmann::json components = nlohmann::json::array();
|
||||
for (const auto& descriptor : descriptors) components.push_back(descriptor->schema());
|
||||
return {{"protocol", "aethera.plot.inspector"}, {"version", 2}, {"components", std::move(components)}};
|
||||
return {{"protocol", "aethera.plot.inspector"}, {"version", 3}, {"components", std::move(components)}};
|
||||
}
|
||||
nlohmann::json write_prop(std::string_view component, std::string_view key, const nlohmann::json& value) override {
|
||||
const auto found = std::ranges::find_if(descriptors, [&](const auto& item) {
|
||||
@@ -53,6 +53,20 @@ public:
|
||||
result["component"] = component;
|
||||
return result;
|
||||
}
|
||||
nlohmann::json component_state(std::string_view component) const override {
|
||||
const auto found = std::ranges::find_if(descriptors, [&](const auto& item) {
|
||||
return item->id() == component;
|
||||
});
|
||||
if (found == descriptors.end())
|
||||
return {{"success", false}, {"error", "unknown component"}};
|
||||
return (*found)->state();
|
||||
}
|
||||
nlohmann::json component_snapshots() const override {
|
||||
nlohmann::json result = nlohmann::json::object();
|
||||
for (const auto& descriptor : descriptors)
|
||||
result[std::string(descriptor->id())] = descriptor->snapshot();
|
||||
return result;
|
||||
}
|
||||
nlohmann::json data_generator_schema() const override {
|
||||
if (!data_generator) return nullptr;
|
||||
return data_generator.schema;
|
||||
@@ -85,15 +99,8 @@ template <typename Object, typename... Fields>
|
||||
std::unique_ptr<detail::Renderable_Descriptor> make_renderable_component(
|
||||
std::string id, std::string label, std::string kind, Object& object) {
|
||||
using Definition = typename Object::Attached_Object;
|
||||
using Tag = typename Definition::Base_Tag;
|
||||
using State = typename Definition::State;
|
||||
using Adapter = detail::Renderable_Adapter<Object, Fields...,
|
||||
detail::Prop_Field < &Renderable_2D::Prop::cache_enabled, "cache_enabled", "Whether this renderable reuses its complete color result across unchanged frames.">,
|
||||
detail::State_Field<Tag, &State::render_dirty, "render_dirty", "Whether the render graph was dirty when evaluated.">,
|
||||
detail::State_Field<Tag, &State::render_executed, "render_executed", "Whether the render graph executed during the latest scene cycle.">,
|
||||
detail::State_Field<Tag, &State::graph_rebuilt, "graph_rebuilt", "Whether the render graph was rebuilt during the latest cycle.">,
|
||||
detail::State_Field<Tag, &State::task_count, "task_count", "Number of tasks in the current render graph.">,
|
||||
detail::State_Field<Tag, &State::execution_time_ns, "execution_time_ns", "Measured render graph execution time in nanoseconds."> >;
|
||||
detail::Prop_Field < &Renderable_2D::Prop::cache_enabled, "cache_enabled", "Whether this renderable reuses its complete color result across unchanged frames.">>;
|
||||
return detail::make_renderable_descriptor(std::move(id), std::move(label), std::move(kind), Adapter{object});
|
||||
}
|
||||
template <typename Scene_Object>
|
||||
@@ -103,14 +110,7 @@ std::unique_ptr<detail::Renderable_Descriptor> make_scene_component(Scene_Object
|
||||
using Adapter = detail::Renderable_Adapter<Scene_Object,
|
||||
detail::Prop_Field < &Prop::viewport, "viewport", "Final scene viewport in physical pixels.">,
|
||||
detail::Prop_Field < &Prop::background, "background", "Scene clear color." >,
|
||||
detail::Prop_Field < &Prop::view_active, "view_active", "Whether the scene publishes rendered frames." >,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_rebuilt, "taskflow_rebuilt", "Whether the scene task graph was rebuilt.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::renderable_count, "renderable_count", "Number of renderables attached to the scene.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_task_count, "taskflow_task_count", "Number of tasks in the scene graph.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_dependency_count, "taskflow_dependency_count", "Number of graph dependencies.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_max_predecessors, "taskflow_max_predecessors", "Maximum direct predecessors of a task.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_max_successors, "taskflow_max_successors", "Maximum direct successors of a task.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_execution_time_ns, "taskflow_execution_time_ns", "Scene graph execution time in nanoseconds."> >;
|
||||
detail::Prop_Field < &Prop::view_active, "view_active", "Whether the scene publishes rendered frames." >>;
|
||||
return detail::make_renderable_descriptor("scene", "场景", "scene", Adapter{scene});
|
||||
}
|
||||
template <typename Axis_Object>
|
||||
|
||||
@@ -225,10 +225,7 @@ public:
|
||||
using State = typename Definition::State;
|
||||
using Scene_Adapter = detail::Renderable_Adapter<Scene_3D,
|
||||
detail::Prop_Field < &Render_Scene_3D::Prop::clear_color, "clear_color", "Linear scene clear color.">,
|
||||
detail::Prop_Field < &Render_Scene_3D::Prop::view_active, "view_active", "Whether the scene publishes rendered frames." >,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::renderable_count, "renderable_count", "Number of renderables attached to the scene.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_task_count, "taskflow_task_count", "Number of tasks in the scene graph.">,
|
||||
detail::State_Field<Scene::Base_Tag, &Scene::State::taskflow_execution_time_ns, "taskflow_execution_time_ns", "Scene graph execution time in nanoseconds."> >;
|
||||
detail::Prop_Field < &Render_Scene_3D::Prop::view_active, "view_active", "Whether the scene publishes rendered frames." >>;
|
||||
using Visual_Adapter = detail::Renderable_Adapter<Visual_Object,
|
||||
detail::Prop_Field < &Prop::transform, "transform", "World transform applied to the complete visual.">,
|
||||
detail::Prop_Field < &Prop::visible, "visible", "Whether the visual participates in rendering." >,
|
||||
@@ -287,7 +284,7 @@ public:
|
||||
[[nodiscard]] nlohmann::json schema() const override {
|
||||
nlohmann::json components = nlohmann::json::array();
|
||||
for (const auto& descriptor : descriptors_) components.push_back(descriptor->schema());
|
||||
return {{"protocol", "aethera.plot.inspector"}, {"version", 2}, {"components", std::move(components)}};
|
||||
return {{"protocol", "aethera.plot.inspector"}, {"version", 3}, {"components", std::move(components)}};
|
||||
}
|
||||
[[nodiscard]] nlohmann::json write_prop(std::string_view component, std::string_view key, const nlohmann::json& value) override {
|
||||
const auto found = std::ranges::find_if(descriptors_, [&](const auto& descriptor) {
|
||||
@@ -298,6 +295,20 @@ public:
|
||||
result["component"] = component;
|
||||
return result;
|
||||
}
|
||||
[[nodiscard]] nlohmann::json component_state(std::string_view component) const override {
|
||||
const auto found = std::ranges::find_if(descriptors_, [&](const auto& descriptor) {
|
||||
return descriptor->id() == component;
|
||||
});
|
||||
if (found == descriptors_.end())
|
||||
return {{"success", false}, {"error", "unknown component"}};
|
||||
return (*found)->state();
|
||||
}
|
||||
[[nodiscard]] nlohmann::json component_snapshots() const override {
|
||||
nlohmann::json result = nlohmann::json::object();
|
||||
for (const auto& descriptor : descriptors_)
|
||||
result[std::string(descriptor->id())] = descriptor->snapshot();
|
||||
return result;
|
||||
}
|
||||
[[nodiscard]] nlohmann::json data_generator_schema() const override {
|
||||
if constexpr (std::same_as<Data_Generator, Random_Data_Generator>) {
|
||||
using Definition = typename Visual_Object::Attached_Object;
|
||||
|
||||
+38
-20
@@ -198,7 +198,9 @@ void append_event_statistics_json(nlohmann::json& output,
|
||||
|
||||
}
|
||||
|
||||
nlohmann::json taskflow_trace_json(const Taskflow_Frame_Trace& trace) {
|
||||
nlohmann::json taskflow_trace_json(
|
||||
const Taskflow_Frame_Trace& trace,
|
||||
const nlohmann::json& component_snapshots) {
|
||||
nlohmann::json markers = nlohmann::json::object();
|
||||
for (const auto& marker : trace.markers)
|
||||
markers[magic_enum::enum_name(marker.marker)] =
|
||||
@@ -218,12 +220,24 @@ nlohmann::json taskflow_trace_json(const Taskflow_Frame_Trace& trace) {
|
||||
nlohmann::json attributes = nlohmann::json::object();
|
||||
for (const auto& [key, value] : node.attributes)
|
||||
attributes[key] = value;
|
||||
nodes.push_back({
|
||||
nlohmann::json encoded{
|
||||
{"native_id", std::to_string(node.native_id)}, {"id", node.node_id},
|
||||
{"parent_id", node.parent_node_id}, {"name", node.name},
|
||||
{"type", node.type}, {"predecessors", std::move(predecessors)},
|
||||
{"successors", std::move(successors)},
|
||||
{"attributes", std::move(attributes)}});
|
||||
{"attributes", std::move(attributes)}};
|
||||
const auto owner = encoded["attributes"].value(
|
||||
"owner_component", std::string{});
|
||||
if (!owner.empty() && component_snapshots.contains(owner)) {
|
||||
const auto& snapshot = component_snapshots.at(owner);
|
||||
encoded["owner"] = {
|
||||
{"component", owner},
|
||||
{"label", snapshot.value("label", owner)},
|
||||
{"kind", snapshot.value("kind", std::string{})}};
|
||||
encoded["prop"] = snapshot.value("prop", nlohmann::json::object());
|
||||
encoded["state"] = snapshot.value("state", nlohmann::json::object());
|
||||
}
|
||||
nodes.push_back(std::move(encoded));
|
||||
}
|
||||
graphs.push_back({
|
||||
{"stage", graph.stage}, {"name", graph.taskflow_name},
|
||||
@@ -374,11 +388,11 @@ struct Plot::Private {
|
||||
/* 高 32 位 requested,低 32 位 captured。每槽只发布一次不可变 Trace,
|
||||
* GET 直接读取已发布槽位,不复制或重排整个历史容器。 */
|
||||
std::atomic_uint64_t taskflow_trace_control{};
|
||||
std::array<std::atomic<std::shared_ptr<const Taskflow_Frame_Trace>>,
|
||||
std::array<std::atomic<std::shared_ptr<const nlohmann::json>>,
|
||||
maximum_taskflow_trace_frames> taskflow_trace_slots{};
|
||||
std::atomic_size_t post_publish_trace_remaining{}; /* 仅捕获 publish 后外接 DAG 的剩余样本。 */
|
||||
std::atomic_uint64_t post_publish_trace_control{}; /* 高 32 位 requested,低 32 位 captured。 */
|
||||
std::array<std::atomic<std::shared_ptr<const Taskflow_Frame_Trace>>,
|
||||
std::array<std::atomic<std::shared_ptr<const nlohmann::json>>,
|
||||
maximum_taskflow_trace_frames> post_publish_trace_slots{};
|
||||
Task_Node completion_tail{}; /* Scene 图内固定停在 plot.frame.publish。 */
|
||||
Task_Graph post_publish_graph{"plot.post_publish"}; /* publish 后外接 DAG;不再占用 Scene render admission。 */
|
||||
@@ -428,13 +442,14 @@ struct Plot::Private {
|
||||
[[nodiscard]] bool mark_taskflow_trace(Render_Frame& frame);
|
||||
[[nodiscard]] bool mark_post_publish_taskflow_trace();
|
||||
void store_trace(std::atomic_uint64_t& control,
|
||||
std::array<std::atomic<std::shared_ptr<const Taskflow_Frame_Trace>>,
|
||||
std::array<std::atomic<std::shared_ptr<const nlohmann::json>>,
|
||||
maximum_taskflow_trace_frames>& slots,
|
||||
const Taskflow_Frame_Trace& trace);
|
||||
const Taskflow_Frame_Trace& trace,
|
||||
const nlohmann::json& component_snapshots = {});
|
||||
[[nodiscard]] nlohmann::json trace_response(
|
||||
const std::atomic_uint64_t& control,
|
||||
const std::atomic_size_t& remaining,
|
||||
const std::array<std::atomic<std::shared_ptr<const Taskflow_Frame_Trace>>,
|
||||
const std::array<std::atomic<std::shared_ptr<const nlohmann::json>>,
|
||||
maximum_taskflow_trace_frames>& slots) const;
|
||||
void fail(std::exception_ptr failure) noexcept;
|
||||
};
|
||||
@@ -610,10 +625,12 @@ bool Plot::Private::mark_post_publish_taskflow_trace() {
|
||||
|
||||
void Plot::Private::store_trace(
|
||||
std::atomic_uint64_t& control,
|
||||
std::array<std::atomic<std::shared_ptr<const Taskflow_Frame_Trace>>,
|
||||
std::array<std::atomic<std::shared_ptr<const nlohmann::json>>,
|
||||
maximum_taskflow_trace_frames>& slots,
|
||||
const Taskflow_Frame_Trace& value) {
|
||||
auto trace = std::make_shared<const Taskflow_Frame_Trace>(value);
|
||||
const Taskflow_Frame_Trace& value,
|
||||
const nlohmann::json& component_snapshots) {
|
||||
auto trace = std::make_shared<const nlohmann::json>(
|
||||
taskflow_trace_json(value, component_snapshots));
|
||||
auto state = control.load(std::memory_order_acquire);
|
||||
for (;;) {
|
||||
const auto requested = static_cast<std::uint32_t>(state >> 32U);
|
||||
@@ -632,7 +649,7 @@ void Plot::Private::store_trace(
|
||||
nlohmann::json Plot::Private::trace_response(
|
||||
const std::atomic_uint64_t& control,
|
||||
const std::atomic_size_t& remaining,
|
||||
const std::array<std::atomic<std::shared_ptr<const Taskflow_Frame_Trace>>,
|
||||
const std::array<std::atomic<std::shared_ptr<const nlohmann::json>>,
|
||||
maximum_taskflow_trace_frames>& slots) const {
|
||||
nlohmann::json frames = nlohmann::json::array();
|
||||
const auto state = control.load(std::memory_order_acquire);
|
||||
@@ -640,7 +657,7 @@ nlohmann::json Plot::Private::trace_response(
|
||||
const auto captured = static_cast<std::uint32_t>(state);
|
||||
for (std::uint32_t index = 0; index < captured; ++index)
|
||||
if (const auto trace = slots[index].load(std::memory_order_acquire))
|
||||
frames.push_back(taskflow_trace_json(*trace));
|
||||
frames.push_back(*trace);
|
||||
const auto left = remaining.load(std::memory_order_acquire);
|
||||
return {
|
||||
{"protocol", "aethera.taskflow.frames"}, {"version", 1},
|
||||
@@ -961,15 +978,12 @@ void Plot::Private::retire_completed_frame(Render_Frame* frame) {
|
||||
|
||||
{
|
||||
std::lock_guard lock(completed_frame_statistics_mutex);
|
||||
completed_frame_statistics_state = completed_frame_statistics.submit(
|
||||
*frame, std::holds_alternative<std::unique_ptr<Frame_2D>>(managed->frame)
|
||||
? Frame_Dimension::two_dimensional
|
||||
: Frame_Dimension::three_dimensional);
|
||||
completed_frame_statistics_state = completed_frame_statistics.submit(*frame);
|
||||
}
|
||||
|
||||
if (frame->taskflow_trace_requested())
|
||||
store_trace(taskflow_trace_control, taskflow_trace_slots,
|
||||
frame->take_taskflow_trace());
|
||||
frame->take_taskflow_trace(), view->component_snapshots());
|
||||
|
||||
auto expected = Frame_State::consuming;
|
||||
if (!managed->state.compare_exchange_strong(
|
||||
@@ -1174,6 +1188,10 @@ nlohmann::json Plot::write_prop(std::string_view component,
|
||||
return result;
|
||||
}
|
||||
|
||||
nlohmann::json Plot::component_state(std::string_view component) const {
|
||||
return d->view->component_state(component);
|
||||
}
|
||||
|
||||
nlohmann::json Plot::generate_data(const nlohmann::json& input) {
|
||||
ensure_started();
|
||||
return d->view->generate_data(input);
|
||||
@@ -1234,7 +1252,7 @@ nlohmann::json Plot::diagnostics() const {
|
||||
const std::size_t byte_length = pacing.video_enabled
|
||||
? static_cast<std::size_t>(stream.width) * stream.height * 4U : 0U;
|
||||
nlohmann::json output{
|
||||
{"protocol", "aethera.plot.diagnostics"}, {"version", 2},
|
||||
{"protocol", "aethera.plot.diagnostics"}, {"version", 3},
|
||||
{"dimension", is_3d ? "3D" : "2D"},
|
||||
{"sequence", identity.sequence},
|
||||
{"correlation_id", identity.correlation_id},
|
||||
@@ -1356,7 +1374,7 @@ nlohmann::json Plot::post_publish_taskflow_trace() const {
|
||||
}
|
||||
|
||||
void Plot::reset_diagnostics() {
|
||||
std::visit([](auto& scene) { scene->reset_frame_statistics(); }, d->scene);
|
||||
std::visit([](auto& scene) { scene->reset_diagnostics(); }, d->scene);
|
||||
std::lock_guard lock(d->completed_frame_statistics_mutex);
|
||||
d->completed_frame_statistics.reset();
|
||||
d->completed_frame_statistics_state = {};
|
||||
|
||||
@@ -65,6 +65,9 @@ public:
|
||||
[[nodiscard]] virtual nlohmann::json write_prop(std::string_view component,
|
||||
std::string_view key,
|
||||
const nlohmann::json& value) = 0;
|
||||
[[nodiscard]] virtual nlohmann::json component_state(
|
||||
std::string_view component) const = 0;
|
||||
[[nodiscard]] virtual nlohmann::json component_snapshots() const = 0;
|
||||
[[nodiscard]] virtual nlohmann::json data_generator_schema() const = 0;
|
||||
[[nodiscard]] virtual nlohmann::json generate_data(const nlohmann::json& input) = 0;
|
||||
virtual void update(const Plot_Render_Tick& tick) = 0;
|
||||
@@ -86,6 +89,7 @@ public:
|
||||
[[nodiscard]] nlohmann::json write_prop(std::string_view component,
|
||||
std::string_view key,
|
||||
const nlohmann::json& value);
|
||||
[[nodiscard]] nlohmann::json component_state(std::string_view component) const;
|
||||
[[nodiscard]] nlohmann::json generate_data(const nlohmann::json& input);
|
||||
[[nodiscard]] nlohmann::json diagnostics() const;
|
||||
/* 清空旧捕获并请求接下来实际完成的 frame_count 帧 Task DAG。 */
|
||||
|
||||
@@ -23,6 +23,8 @@ public:
|
||||
virtual ~Renderable_Descriptor() = default;
|
||||
[[nodiscard]] virtual std::string_view id() const noexcept = 0;
|
||||
[[nodiscard]] virtual nlohmann::json schema() const = 0;
|
||||
[[nodiscard]] virtual nlohmann::json state() const = 0;
|
||||
[[nodiscard]] virtual nlohmann::json snapshot() const = 0;
|
||||
[[nodiscard]] virtual nlohmann::json write_prop(std::string_view key, const nlohmann::json& value) = 0;
|
||||
};
|
||||
template <typename Adapter>
|
||||
@@ -31,6 +33,8 @@ public:
|
||||
Renderable_Descriptor_Model(std::string id, std::string label, std::string kind, Adapter adapter);
|
||||
[[nodiscard]] std::string_view id() const noexcept override;
|
||||
[[nodiscard]] nlohmann::json schema() const override;
|
||||
[[nodiscard]] nlohmann::json state() const override;
|
||||
[[nodiscard]] nlohmann::json snapshot() const override;
|
||||
[[nodiscard]] nlohmann::json write_prop(std::string_view key, const nlohmann::json& value) override;
|
||||
private:
|
||||
std::string component_id;
|
||||
|
||||
@@ -44,6 +44,11 @@ template <typename Object, typename... Fields>
|
||||
struct Renderable_Adapter final : public structive::Property_Object<Renderable_Adapter<Object, Fields...>, structive::No_Lock_Policy> {
|
||||
public:
|
||||
explicit Renderable_Adapter(Object& value) : object(&value) {}
|
||||
void identify(std::string_view id) {
|
||||
if constexpr (std::derived_from<Object, aethera::Renderable>)
|
||||
double_buffer::detail::Internal_Access::layer<aethera::Renderable>(object)
|
||||
.diagnostic_component_id = id;
|
||||
}
|
||||
private:
|
||||
template <typename Adapter, typename Field>
|
||||
friend struct Renderable_Field_Accessor;
|
||||
@@ -287,15 +292,7 @@ inline std::string_view protocol_field_label(std::string_view key) {
|
||||
{"selected_regions", "已选区域"}, {"cache_enabled", "启用绘制缓存"},
|
||||
{"transform", "空间变换"}, {"visible", "是否可见"},
|
||||
{"depth_test", "深度测试"}, {"items", "项目数据"},
|
||||
{"render_dirty", "渲染图待更新"},
|
||||
{"render_executed", "渲染图已执行"},
|
||||
{"graph_rebuilt", "渲染图已重建"},
|
||||
{"task_count", "任务数量"},
|
||||
{"execution_time_ns", "渲染耗时"},
|
||||
{"taskflow_rebuilt", "场景任务图已重建"}, {"renderable_count", "可渲染对象数量"},
|
||||
{"taskflow_task_count", "场景任务数量"}, {"taskflow_dependency_count", "任务依赖数量"},
|
||||
{"taskflow_max_predecessors", "最大前驱数量"}, {"taskflow_max_successors", "最大后继数量"},
|
||||
{"taskflow_execution_time_ns", "场景执行耗时"}, {"next_tick", "下一时间刻度"},
|
||||
{"next_tick", "下一时间刻度"},
|
||||
{"sample_count", "样本数量"}, {"rendered_point_count", "已绘制点数量"},
|
||||
{"selectable_marker_count", "可选标记数量"}, {"stored_block_count", "已保存数据块数量"},
|
||||
{"stored_point_count", "已保存数据点数量"}, {"history_count", "历史帧数量"},
|
||||
@@ -312,11 +309,9 @@ inline std::string_view protocol_field_label(std::string_view key) {
|
||||
template <typename Adapter>
|
||||
nlohmann::json adapter_schema(const Adapter& adapter, std::string_view id, std::string_view label, std::string_view kind) {
|
||||
nlohmann::json fields = nlohmann::json::array();
|
||||
nlohmann::json state = nlohmann::json::object();
|
||||
structive::type_descriptor<Adapter>().for_each_property([&](auto, const auto& field) {
|
||||
using Field = std::remove_cvref_t<decltype(field)>;
|
||||
using Value = typename Field::value_type;
|
||||
const auto value = field.accessor.read(adapter);
|
||||
const bool editable = field.template attribute<Renderable_Field_Role_Category>().value == Renderable_Field_Role::prop;
|
||||
const auto field_label = protocol_field_label(field.key());
|
||||
std::string_view editor = protocol_editor<Value>();
|
||||
@@ -327,9 +322,9 @@ nlohmann::json adapter_schema(const Adapter& adapter, std::string_view id, std::
|
||||
{"key", field.key()}, {"label", field_label}, {"editor", editor},
|
||||
{"editable", editable},
|
||||
{"description", std::string(field_label) + "。协议字段:" + std::string(field.key()) + "。"},
|
||||
{"technical_description", std::string(Field::accessor_type::description.view())},
|
||||
{"value", encode_protocol_value(value)}
|
||||
{"technical_description", std::string(Field::accessor_type::description.view())}
|
||||
};
|
||||
if (editable) item["value"] = encode_protocol_value(field.accessor.read(adapter));
|
||||
if constexpr (std::same_as<Value, render_3d::Linear_Color>) item["color_channel_scale"] = "normalized";
|
||||
else if constexpr (std::same_as<Value, Color>) item["color_channel_scale"] = "byte";
|
||||
if (editable) {
|
||||
@@ -341,17 +336,23 @@ nlohmann::json adapter_schema(const Adapter& adapter, std::string_view id, std::
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
state[field.key()] = encode_protocol_value(value);
|
||||
}
|
||||
fields.push_back(std::move(item));
|
||||
});
|
||||
return {
|
||||
{"id", id}, {"label", label}, {"kind", kind},
|
||||
{"fields", std::move(fields)}, {"state", std::move(state)}
|
||||
{"fields", std::move(fields)}
|
||||
};
|
||||
}
|
||||
template <typename Adapter>
|
||||
nlohmann::json adapter_values(const Adapter& adapter, Renderable_Field_Role role) {
|
||||
nlohmann::json result = nlohmann::json::object();
|
||||
structive::type_descriptor<Adapter>().for_each_property([&](auto, const auto& field) {
|
||||
if (field.template attribute<Renderable_Field_Role_Category>().value == role)
|
||||
result[field.key()] = encode_protocol_value(field.accessor.read(adapter));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
template <typename Adapter>
|
||||
nlohmann::json write_adapter_prop(Adapter& adapter, std::string_view key, const nlohmann::json& input) {
|
||||
nlohmann::json result{{"success", false}, {"key", key}};
|
||||
const bool found = structive::visit_schema_property(structive::type_descriptor<Adapter>(), key, [&](auto, const auto& field) {
|
||||
@@ -382,7 +383,9 @@ nlohmann::json write_adapter_prop(Adapter& adapter, std::string_view key, const
|
||||
}
|
||||
template <typename Adapter>
|
||||
Renderable_Descriptor_Model<Adapter>::Renderable_Descriptor_Model(
|
||||
std::string id, std::string label, std::string kind, Adapter value) : component_id(std::move(id)), component_label(std::move(label)), component_kind(std::move(kind)), adapter(std::move(value)) {}
|
||||
std::string id, std::string label, std::string kind, Adapter value) : component_id(std::move(id)), component_label(std::move(label)), component_kind(std::move(kind)), adapter(std::move(value)) {
|
||||
adapter.identify(component_id);
|
||||
}
|
||||
template <typename Adapter>
|
||||
std::string_view Renderable_Descriptor_Model<Adapter>::id() const noexcept {
|
||||
return component_id;
|
||||
@@ -392,6 +395,19 @@ nlohmann::json Renderable_Descriptor_Model<Adapter>::schema() const {
|
||||
return adapter_schema(adapter, component_id, component_label, component_kind);
|
||||
}
|
||||
template <typename Adapter>
|
||||
nlohmann::json Renderable_Descriptor_Model<Adapter>::state() const {
|
||||
return {{"protocol", "aethera.component.state"}, {"version", 1},
|
||||
{"component", component_id},
|
||||
{"state", adapter_values(adapter, Renderable_Field_Role::state)}};
|
||||
}
|
||||
template <typename Adapter>
|
||||
nlohmann::json Renderable_Descriptor_Model<Adapter>::snapshot() const {
|
||||
return {{"component", component_id}, {"label", component_label},
|
||||
{"kind", component_kind},
|
||||
{"prop", adapter_values(adapter, Renderable_Field_Role::prop)},
|
||||
{"state", adapter_values(adapter, Renderable_Field_Role::state)}};
|
||||
}
|
||||
template <typename Adapter>
|
||||
nlohmann::json Renderable_Descriptor_Model<Adapter>::write_prop(std::string_view key, const nlohmann::json& value) {
|
||||
return write_adapter_prop(adapter, key, value);
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
|
||||
namespace aethera::web {
|
||||
[[nodiscard]] nlohmann::json taskflow_trace_json(
|
||||
const Taskflow_Frame_Trace& trace);
|
||||
const Taskflow_Frame_Trace& trace,
|
||||
const nlohmann::json& component_snapshots = {});
|
||||
}
|
||||
|
||||
@@ -442,6 +442,28 @@ int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root)
|
||||
}
|
||||
}, {drogon::Put});
|
||||
|
||||
app.registerHandler("/plot/{1}/component/{2}/state", [plots](
|
||||
const drogon::HttpRequestPtr&,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& callback,
|
||||
std::string plot_id,
|
||||
std::string component) {
|
||||
auto plot = find_plot(*plots, plot_id);
|
||||
if (!plot) {
|
||||
callback(error_response(drogon::k404NotFound, "unknown plot"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto result = plot->component_state(component);
|
||||
if (result.value("success", true)) callback(json_response(std::move(result)));
|
||||
else callback(error_response(drogon::k404NotFound,
|
||||
result.value("error", "unknown component")));
|
||||
}
|
||||
catch (const std::exception& failure) {
|
||||
callback(error_response(drogon::k500InternalServerError,
|
||||
failure.what()));
|
||||
}
|
||||
}, {drogon::Get});
|
||||
|
||||
app.registerHandler("/plot/{1}/data/generate", [plots](
|
||||
const drogon::HttpRequestPtr& request,
|
||||
std::function<void(const drogon::HttpResponsePtr&)>&& callback,
|
||||
|
||||
Reference in New Issue
Block a user