#pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace aethera::web::detail { struct Renderable_Field_Role_Category {}; template struct Renderable_Field_Role_Attribute { using attribute_category = Renderable_Field_Role_Category; static constexpr bool single_valued = true; static constexpr bool inheritable = false; static constexpr auto value = Role; }; template struct Prop_Field { static constexpr auto member = Member; static constexpr auto key = Key; static constexpr auto description = Description; static constexpr auto editor = Editor; static constexpr auto role = Renderable_Field_Role::prop; }; template struct State_Field { using tag_type = Tag; static constexpr auto member = Member; static constexpr auto key = Key; static constexpr auto description = Description; static constexpr auto role = Renderable_Field_Role::state; }; template struct Renderable_Adapter final : public structive::Property_Object, structive::No_Lock_Policy> { public: explicit Renderable_Adapter(Object& value) : object(&value) {} void identify(std::string_view id) { if constexpr (std::derived_from) double_buffer::detail::Internal_Access::layer(object) .diagnostic_component_id = id; } private: template friend struct Renderable_Field_Accessor; Object* object; }; template struct Renderable_Field_Accessor; template struct Renderable_Field_Accessor, Prop_Field> { using object_type = Renderable_Adapter; using value_type = typename structive::Member_Pointer_Traits::value_type; using storage_identity = void; using dependency_spec = structive::No_Property_Dependencies; static constexpr auto description = Description; static constexpr auto editor = Editor; static constexpr bool readable = true; static constexpr bool writable = true; static constexpr bool synchronized_view_read = false; static constexpr bool trusted_object_access = true; [[nodiscard]] value_type read(const object_type& adapter) const { return adapter.object->template get(); } void write(object_type& adapter, value_type value) const { adapter.object->template set(std::move(value)); } }; template struct Renderable_Field_Accessor, State_Field> { using object_type = Renderable_Adapter; using value_type = typename structive::Member_Pointer_Traits::value_type; using storage_identity = void; using dependency_spec = structive::No_Property_Dependencies; static constexpr auto description = Description; static constexpr bool readable = true; static constexpr bool writable = false; static constexpr bool synchronized_view_read = false; static constexpr bool trusted_object_access = true; [[nodiscard]] value_type read(const object_type& adapter) const { value_type result{}; adapter.object->template access_state([&](const auto& state) { result = state.*Member; }); return result; } }; template constexpr auto renderable_field_descriptor() { using Accessor = Renderable_Field_Accessor; using Role = Renderable_Field_Role_Attribute; using Key = structive::Key_Attribute; return structive::Property_Descriptor{{}, {Key{}, Role{}}}; } } namespace structive { template struct Type_Descriptor> { static auto get() { using Adapter = aethera::web::detail::Renderable_Adapter; return object(synchronization(sync_all_unsynchronized), aethera::web::detail::renderable_field_descriptor()...); } }; } namespace aethera::web::detail { template [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return mcp::protocol_editor(); } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "color"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "color"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "point2"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "size"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "size"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "rect"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "range"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "partition-grid"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "pen"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "brush"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "font"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "color-map"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "vector3"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag) noexcept { return "matrix4"; } [[nodiscard]] constexpr std::string_view renderable_editor( mcp::Type_Tag>) noexcept { return "marker-list"; } inline std::string_view protocol_field_label(std::string_view key) { static constexpr std::pair labels[] = { {"position", "位置"}, {"viewport", "视口尺寸"}, {"background", "背景颜色"}, {"clear_color", "清屏颜色"}, {"view_active", "启用视图"}, {"initial_view", "初始相机视图"}, {"projection", "投影方式"}, {"controller", "Datoviz 相机模式"}, {"turntable_control", "Turntable 控制"}, {"arcball_control", "Arcball 控制"}, {"fly_control", "Fly 控制"}, {"panzoom_control", "Panzoom 控制"}, {"vertical_field_of_view_degrees", "垂直视场角"}, {"near_plane", "近裁剪面"}, {"far_plane", "远裁剪面"}, {"x_axis", "X 坐标轴"}, {"y_axis", "Y 坐标轴"}, {"z_axis", "Z 坐标轴"}, {"pixel_length", "轴线长度"}, {"orientation", "轴线方向"}, {"tick_length", "主刻度长度"}, {"sub_tick_length", "次刻度长度"}, {"axis_pen", "轴线画笔"}, {"unit_text", "单位文字"}, {"unit_text_font", "单位字体"}, {"unit_text_pen", "单位文字画笔"}, {"unit_text_background_brush", "单位文字背景"}, {"label_rotation_degrees", "标签旋转角度"}, {"coordinate_range", "坐标范围"}, {"precision", "小数精度"}, {"locale", "数字区域格式"}, {"wheel_enabled", "允许滚轮缩放"}, {"drag_enabled", "允许拖动平移"}, {"visible_count", "可见数量"}, {"tick_label_spacing_px", "刻度文字间距"}, {"estimated_label_width_px", "预计标签宽度"}, {"format", "时间格式"}, {"newest_at_start", "最新数据置于起点"}, {"center_frequency", "中心频率"}, {"partition_count", "分区数量"}, {"partition_grid", "分区网格"}, {"interpolation_mode", "插值模式"}, {"visible_range_only", "仅处理可见范围"}, {"frequency_range", "频率范围"}, {"power_range", "功率范围"}, {"i_range", "同相范围"}, {"q_range", "正交范围"}, {"phase_offset_radians", "相位偏移"}, {"pen", "画笔"}, {"samples", "样本数据"}, {"rows", "瀑布图行数据"}, {"color_map", "颜色映射"}, {"tooltip_enabled", "启用提示框"}, {"tooltip_font", "提示框字体"}, {"tooltip_text_pen", "提示文字画笔"}, {"tooltip_background_brush", "提示框背景"}, {"frequency_bin_count", "频率箱数量"}, {"frequency_point_size", "频率网格数量"}, {"power_point_size", "功率网格数量"}, {"attenuation_rate", "衰减速率"}, {"interpolate", "启用插值"}, {"spectra", "频谱历史"}, {"blocks", "扫频块数据"}, {"bins_per_block", "每块频点数"}, {"block_count", "扫频块数量"}, {"max_hold_visible", "显示最大保持"}, {"min_hold_visible", "显示最小保持"}, {"max_marker_visible", "显示最大标记"}, {"min_marker_visible", "显示最小标记"}, {"sweep_region_visible", "显示扫频区域"}, {"sweep_frequency_range", "扫频范围"}, {"max_brush", "最大保持画刷"}, {"current_brush", "当前曲线画刷"}, {"min_brush", "最小保持画刷"}, {"max_pen", "最大保持画笔"}, {"current_pen", "当前曲线画笔"}, {"min_pen", "最小保持画笔"}, {"selected_marker_pen", "选中标记画笔"}, {"marker_pen", "标记画笔"}, {"middle_frequency_pen", "中心频率画笔"}, {"sweep_region_brush", "扫频区域画刷"}, {"current_frequency_pen", "当前频率指示线"}, {"custom_markers", "自定义标记"}, {"selected_marker", "当前选中标记"}, {"point_lifetime_ms", "点保留时间"}, {"type", "星座类型"}, {"point_color", "数据点颜色"}, {"anchor_color", "参考点颜色"}, {"points", "星座点数据"}, {"label_font", "标签字体"}, {"label_pen", "标签画笔"}, {"selection_brush", "选区画刷"}, {"selection_border_pen", "选区边框画笔"}, {"selected_regions", "已选区域"}, {"cache_enabled", "启用绘制缓存"}, {"transform", "空间变换"}, {"visible", "是否可见"}, {"depth_test", "深度测试"}, {"items", "项目数据"}, {"next_tick", "下一时间刻度"}, {"sample_count", "样本数量"}, {"rendered_point_count", "已绘制点数量"}, {"selectable_marker_count", "可选标记数量"}, {"stored_block_count", "已保存数据块数量"}, {"stored_point_count", "已保存数据点数量"}, {"history_count", "历史帧数量"}, {"latest_spectrum_point_count", "最新频谱点数量"}, {"rendered_cell_count", "已绘制单元数量"}, {"row_count", "瀑布图行数量"}, {"point_count", "数据点数量"}, {"selected_region_count", "已选区域数量"}, {"item_count", "项目数量"}, {"prepared_item_count", "已准备项目数量"}, {"prepared_revision", "已准备修订号"} }; const auto found = std::ranges::find_if(labels, [key](const auto& entry) { return entry.first == key; }); return found == std::end(labels) ? key : found->second; } template 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(); structive::type_descriptor().for_each_property([&](auto, const auto& field) { using Field = std::remove_cvref_t; using Value = typename Field::value_type; const bool editable = field.template attribute().value == Renderable_Field_Role::prop; const auto field_label = protocol_field_label(field.key()); std::string_view editor = renderable_editor(mcp::Type_Tag{}); if constexpr (requires { Field::accessor_type::editor; }) { if (!Field::accessor_type::editor.view().empty()) editor = Field::accessor_type::editor.view(); } nlohmann::json item{ {"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())} }; if (editable) item["value"] = mcp::encode_protocol_value(field.accessor.read(adapter)); if constexpr (std::same_as) item["color_channel_scale"] = "normalized"; else if constexpr (std::same_as) item["color_channel_scale"] = "byte"; if (editable) { if constexpr (std::is_enum_v) { item["options"] = nlohmann::json::array(); for (const auto enum_value : magic_enum::enum_values()) { const auto name = magic_enum::enum_name(enum_value); item["options"].push_back({{"value", name}, {"label", name}}); } } } fields.push_back(std::move(item)); }); return { {"id", id}, {"label", label}, {"kind", kind}, {"fields", std::move(fields)} }; } template nlohmann::json adapter_values(const Adapter& adapter, Renderable_Field_Role role) { nlohmann::json result = nlohmann::json::object(); structive::type_descriptor().for_each_property([&](auto, const auto& field) { if (field.template attribute().value == role) result[field.key()] = mcp::encode_protocol_value(field.accessor.read(adapter)); }); return result; } template 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(), key, [&](auto, const auto& field) { using Field = std::remove_cvref_t; using Value = typename Field::value_type; if constexpr (!Field::writable) { result["error"] = "property is read-only"; } else { try { Value value{}; mcp::decode_protocol_value(value, input); const auto status = adapter.runtime_write(key, typeid(Value), &value); if (status != structive::Runtime_Access_Result::ok) { result["error"] = "property write was rejected"; return; } result["success"] = true; result["value"] = mcp::encode_protocol_value(field.accessor.read(adapter)); } catch (const std::exception& error) { result["error"] = error.what(); } } }); if (!found) result["error"] = "unknown property"; return result; } template Renderable_Descriptor_Model::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)) { adapter.identify(component_id); } template std::string_view Renderable_Descriptor_Model::id() const noexcept { return component_id; } template nlohmann::json Renderable_Descriptor_Model::schema() const { return adapter_schema(adapter, component_id, component_label, component_kind); } template nlohmann::json Renderable_Descriptor_Model::state() const { return {{"protocol", "aethera.component.state"}, {"version", 1}, {"component", component_id}, {"state", adapter_values(adapter, Renderable_Field_Role::state)}}; } template nlohmann::json Renderable_Descriptor_Model::values() 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 nlohmann::json Renderable_Descriptor_Model::write_prop(std::string_view key, const nlohmann::json& value) { return write_adapter_prop(adapter, key, value); } template std::unique_ptr make_renderable_descriptor( std::string id, std::string label, std::string kind, Adapter adapter) { return std::make_unique < Renderable_Descriptor_Model > ( std::move(id), std::move(label), std::move(kind), std::move(adapter)); } }