更完
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
#pragma once
|
||||
|
||||
#include "renderive/scene/Scene2D_Context.hpp"
|
||||
#include <structive/property/property.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
enum class Gallery_Frame_Mode : std::uint8_t {
|
||||
Manual,
|
||||
Low_Latency,
|
||||
Playback
|
||||
};
|
||||
|
||||
struct Gallery_Action_Category {};
|
||||
|
||||
struct Gallery_Action_Attribute {
|
||||
using attribute_category = Gallery_Action_Category;
|
||||
static constexpr bool single_valued = false;
|
||||
static constexpr bool inheritable = false;
|
||||
|
||||
std::string_view id;
|
||||
std::string_view label;
|
||||
std::string_view api;
|
||||
std::string_view group;
|
||||
std::string_view description;
|
||||
std::string_view argument_input;
|
||||
std::string_view argument_label;
|
||||
double argument_default{};
|
||||
bool request_frame{};
|
||||
};
|
||||
|
||||
constexpr Gallery_Action_Attribute gallery_action(
|
||||
std::string_view id, std::string_view label, std::string_view api,
|
||||
std::string_view group, std::string_view description = {},
|
||||
std::string_view argument_input = {}, std::string_view argument_label = {},
|
||||
double argument_default = 0.0, bool request_frame = false) {
|
||||
return {id, label, api, group, description, argument_input, argument_label,
|
||||
argument_default, request_frame};
|
||||
}
|
||||
|
||||
struct Gallery_Action_Model {
|
||||
std::string id;
|
||||
std::string label;
|
||||
std::string api;
|
||||
std::string description;
|
||||
std::string group;
|
||||
std::string argument_input;
|
||||
std::string argument_label;
|
||||
double argument_default{};
|
||||
bool request_frame{};
|
||||
};
|
||||
|
||||
inline Gallery_Action_Model gallery_action_model(
|
||||
const Gallery_Action_Attribute& action) {
|
||||
return {std::string(action.id), std::string(action.label), std::string(action.api),
|
||||
std::string(action.description), std::string(action.group),
|
||||
std::string(action.argument_input), std::string(action.argument_label),
|
||||
action.argument_default, action.request_frame};
|
||||
}
|
||||
|
||||
template <structive::Property_Described_Object Object>
|
||||
void append_gallery_actions(std::vector<Gallery_Action_Model>& target) {
|
||||
structive::type_descriptor<Object>()
|
||||
.template for_each_type_attribute<Gallery_Action_Category>(
|
||||
[&target](const auto& action) {
|
||||
const auto found = std::find_if(
|
||||
target.begin(), target.end(),
|
||||
[&action](const auto& existing) { return existing.id == action.id; });
|
||||
if (found == target.end())
|
||||
target.push_back(gallery_action_model(action));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
namespace structive {
|
||||
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
struct Type_Descriptor<Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>> {
|
||||
using T = Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>;
|
||||
static auto get() {
|
||||
using renderive::web::gallery_action;
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("mode_prepare", "准备帧",
|
||||
"Manual_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
|
||||
"Manual_Refresh_Strategy"),
|
||||
gallery_action("mode_refresh", "提交手动刷新",
|
||||
"Manual_Refresh_Strategy::refresh / Plot_Core::refresh_manual_frame",
|
||||
"Manual_Refresh_Strategy"),
|
||||
gallery_action("mode_render", "渲染已刷新帧",
|
||||
"Manual_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
|
||||
"Manual_Refresh_Strategy", {}, {}, {}, 0.0, true),
|
||||
gallery_action("mode_discard", "丢弃待刷新帧",
|
||||
"Plot_Core::discard_pending_frame",
|
||||
"Manual_Refresh_Strategy"),
|
||||
gallery_action("mode_cycle", "执行完整手动帧周期",
|
||||
"Plot_Core::render_frame", "Manual_Refresh_Strategy",
|
||||
{}, {}, {}, 0.0, true)));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
struct Type_Descriptor<Low_Latency_Strategy<Scene_Frame, Mutex, Observer>> {
|
||||
using T = Low_Latency_Strategy<Scene_Frame, Mutex, Observer>;
|
||||
static auto get() {
|
||||
using renderive::web::gallery_action;
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("mode_prepare", "发布低延迟帧",
|
||||
"Low_Latency_Strategy::acquire_painter / Plot_Core::prepare_frame",
|
||||
"Low_Latency_Strategy"),
|
||||
gallery_action("mode_render", "消费最新帧",
|
||||
"Low_Latency_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
|
||||
"Low_Latency_Strategy", {}, {}, {}, 0.0, true),
|
||||
gallery_action("mode_discard", "丢弃陈旧帧",
|
||||
"Plot_Core::discard_pending_frame", "Low_Latency_Strategy"),
|
||||
gallery_action("mode_cycle", "执行低延迟帧周期",
|
||||
"Plot_Core::render_frame", "Low_Latency_Strategy",
|
||||
{}, {}, {}, 0.0, true)));
|
||||
}
|
||||
};
|
||||
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
struct Type_Descriptor<Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>> {
|
||||
using T = Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>;
|
||||
static auto get() {
|
||||
using renderive::web::gallery_action;
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("mode_enqueue", "压入一帧",
|
||||
"Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
|
||||
"Flow_Refresh_Strategy"),
|
||||
gallery_action("mode_enqueue_burst", "批量压入回放队列",
|
||||
"Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame x N",
|
||||
"Flow_Refresh_Strategy", {}, "number", "帧数", 8.0),
|
||||
gallery_action("mode_dequeue", "消费队首帧",
|
||||
"Flow_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
|
||||
"Flow_Refresh_Strategy", {}, {}, {}, 0.0, true),
|
||||
gallery_action("mode_cycle", "执行回放帧周期",
|
||||
"Plot_Core::render_frame", "Flow_Refresh_Strategy",
|
||||
{}, {}, {}, 0.0, true)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace structive
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
inline std::vector<Gallery_Action_Model> gallery_frame_actions(Gallery_Frame_Mode mode) {
|
||||
std::vector<Gallery_Action_Model> result;
|
||||
switch (mode) {
|
||||
case Gallery_Frame_Mode::Manual:
|
||||
append_gallery_actions<Manual_Refresh_Strategy<Scene2D_Frame_Data, std::mutex,
|
||||
Observer_State<>>>(result);
|
||||
break;
|
||||
case Gallery_Frame_Mode::Low_Latency:
|
||||
append_gallery_actions<Low_Latency_Strategy<Scene2D_Frame_Data, std::mutex,
|
||||
Observer_State<>>>(result);
|
||||
break;
|
||||
case Gallery_Frame_Mode::Playback:
|
||||
append_gallery_actions<Flow_Refresh_Strategy<Scene2D_Frame_Data, std::mutex,
|
||||
Observer_State<>>>(result);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
@@ -31,21 +31,7 @@ struct Case_Model {
|
||||
int preferred_height = 320;
|
||||
};
|
||||
|
||||
struct Action_Model {
|
||||
std::string id;
|
||||
std::string label;
|
||||
std::string api;
|
||||
std::string description;
|
||||
std::string group;
|
||||
std::string argument_input;
|
||||
std::string argument_label;
|
||||
double argument_default{};
|
||||
bool request_frame{};
|
||||
};
|
||||
|
||||
struct Action_Definition {
|
||||
Action_Model model;
|
||||
};
|
||||
using Action_Model = Gallery_Action_Model;
|
||||
|
||||
const std::vector<Case_Model>& cases() {
|
||||
static const std::vector<Case_Model> value{
|
||||
@@ -69,103 +55,6 @@ const std::vector<Case_Model>& cases() {
|
||||
return value;
|
||||
}
|
||||
|
||||
Action_Definition action(std::string id, std::string label, std::string api,
|
||||
std::string group, std::string description = {},
|
||||
std::string argument_input = {}, std::string argument_label = {},
|
||||
double argument_default = 0) {
|
||||
return {{std::move(id), std::move(label), std::move(api), std::move(description),
|
||||
std::move(group), std::move(argument_input), std::move(argument_label),
|
||||
argument_default}};
|
||||
}
|
||||
|
||||
Action_Definition frame_action(std::string id, std::string label, std::string api,
|
||||
std::string group) {
|
||||
auto result = action(std::move(id), std::move(label), std::move(api), std::move(group));
|
||||
result.model.request_frame = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Action_Definition> actions(std::string_view case_id,
|
||||
Gallery_Frame_Mode frame_mode) {
|
||||
std::vector<Action_Definition> result{
|
||||
action("reset", "恢复本图默认值", "Gallery_Scene::rebuild", "会话"),
|
||||
action("toggle_view", "切换 View 生命周期", "Plot_Core::activate_view / deactivate_view", "会话"),
|
||||
action("axis_probe", "读取坐标映射与刻度", "coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label", "Axis")
|
||||
};
|
||||
if (frame_mode == Gallery_Frame_Mode::Manual) {
|
||||
result.push_back(action("mode_prepare", "准备帧", "Manual_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
|
||||
"Manual_Refresh_Strategy"));
|
||||
result.push_back(action("mode_refresh", "提交手动刷新", "Manual_Refresh_Strategy::refresh / Plot_Core::refresh_manual_frame",
|
||||
"Manual_Refresh_Strategy"));
|
||||
result.push_back(frame_action("mode_render", "渲染已刷新帧", "Manual_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
|
||||
"Manual_Refresh_Strategy"));
|
||||
result.push_back(action("mode_discard", "丢弃待刷新帧", "Plot_Core::discard_pending_frame",
|
||||
"Manual_Refresh_Strategy"));
|
||||
result.push_back(frame_action("mode_cycle", "执行完整手动帧周期", "Plot_Core::render_frame",
|
||||
"Manual_Refresh_Strategy"));
|
||||
} else if (frame_mode == Gallery_Frame_Mode::Low_Latency) {
|
||||
result.push_back(action("mode_prepare", "发布低延迟帧", "Low_Latency_Strategy::acquire_painter / Plot_Core::prepare_frame",
|
||||
"Low_Latency_Strategy"));
|
||||
result.push_back(frame_action("mode_render", "消费最新帧", "Low_Latency_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
|
||||
"Low_Latency_Strategy"));
|
||||
result.push_back(action("mode_discard", "丢弃陈旧帧", "Plot_Core::discard_pending_frame",
|
||||
"Low_Latency_Strategy"));
|
||||
result.push_back(frame_action("mode_cycle", "执行低延迟帧周期", "Plot_Core::render_frame",
|
||||
"Low_Latency_Strategy"));
|
||||
} else {
|
||||
result.push_back(action("mode_enqueue", "压入一帧", "Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame",
|
||||
"Flow_Refresh_Strategy"));
|
||||
result.push_back(action("mode_enqueue_burst", "批量压入回放队列", "Flow_Refresh_Strategy::acquire_painter / Plot_Core::prepare_frame × N",
|
||||
"Flow_Refresh_Strategy", {}, "number", "帧数", 8));
|
||||
result.push_back(frame_action("mode_dequeue", "消费队首帧", "Flow_Refresh_Strategy::acquire_renderer / Plot_Core::render_prepared_frame",
|
||||
"Flow_Refresh_Strategy"));
|
||||
result.push_back(frame_action("mode_cycle", "执行回放帧周期", "Plot_Core::render_frame",
|
||||
"Flow_Refresh_Strategy"));
|
||||
}
|
||||
if (case_id == "axis_lab") {
|
||||
result.push_back(action("append_time", "追加时间点", "Time_Axis::append_time / tick_to_time", "Time_Axis"));
|
||||
result.push_back(action("read_data_shape", "读取轴采样点状态", "Abs_Axis::transform / Time_Axis::time_point_count", "观察者"));
|
||||
} else if (case_id == "spectrum" || case_id == "selection_overlay") {
|
||||
result.push_back(action("push_samples", "推送一帧样本", "Spectrum::update_samples", "Spectrum"));
|
||||
result.push_back(action("power_at", "查询指定频率功率", "Spectrum::power_at", "Spectrum", {}, "number", "频率", 98e6));
|
||||
result.push_back(action("add_marker", "添加点 Marker", "Spectrum::add_custom_marker", "Marker", {}, "number", "频率", 96e6));
|
||||
result.push_back(action("add_line_marker", "添加线 Marker", "Spectrum::add_custom_line_marker", "Marker", {}, "number", "频率", 100e6));
|
||||
result.push_back(action("remove_marker", "按频率删除 Marker", "Spectrum::remove_custom_marker", "Marker", {}, "number", "频率", 96e6));
|
||||
result.push_back(action("remove_selected_marker", "删除选中 Marker", "Spectrum::remove_selected_marker", "Marker"));
|
||||
result.push_back(action("clear_markers", "清空 Marker", "Spectrum::clear_custom_markers", "Marker"));
|
||||
result.push_back(action("select_marker", "选择 Marker 索引", "Spectrum::set_selected_marker_index", "Marker", {}, "number", "索引", 0));
|
||||
result.push_back(action("select_next_marker", "选择下一个 Marker", "Spectrum::select_next_marker", "Marker"));
|
||||
result.push_back(action("select_previous_marker", "选择上一个 Marker", "Spectrum::select_previous_marker", "Marker"));
|
||||
result.push_back(action("clear_marker_selection", "清除 Marker 选择", "Spectrum::clear_marker_selection", "Marker"));
|
||||
result.push_back(action("set_marker_frequency", "修改选中 Marker 频率",
|
||||
"Spectrum::set_marker_frequency / set_current_marker_frequency",
|
||||
"Marker", {}, "number", "频率", 99e6));
|
||||
result.push_back(action("read_data_shape", "读取输入与绘制点数", "Spectrum::sample_count / rendered_point_count", "观察者"));
|
||||
if (case_id == "selection_overlay") {
|
||||
result.push_back(action("clear_selection", "清空框选区域", "Selection_Rectangle_Overlay::clear_selected_regions", "Selection Overlay"));
|
||||
}
|
||||
} else if (case_id == "waterfall") {
|
||||
result.push_back(action("append_row", "追加一行", "Waterfall::append_row", "Waterfall"));
|
||||
result.push_back(action("append_tick_row", "按 tick 追加一行", "Time_Axis::append_time / Waterfall::append_row(int, span)", "Waterfall"));
|
||||
result.push_back(action("read_data_shape", "读取行、点与渲染单元", "Waterfall::row_count / stored_point_count / rendered_cell_count", "观察者"));
|
||||
} else if (case_id == "afterglow") {
|
||||
result.push_back(action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "Afterglow"));
|
||||
result.push_back(action("read_data_shape", "读取历史帧与渲染网格", "Afterglow::history_count / latest_spectrum_point_count / rendered_cell_count", "观察者"));
|
||||
} else if (case_id == "sweep_spectrum") {
|
||||
result.push_back(action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "Sweep Spectrum"));
|
||||
result.push_back(action("read_data_shape", "读取块与绘制点数", "Sweep_Spectrum::stored_block_count / stored_point_count / rendered_point_count", "观察者"));
|
||||
} else if (case_id == "frequency_trace") {
|
||||
result.push_back(action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "Frequency Trace"));
|
||||
result.push_back(action("append_tick_sample", "按 tick 追加样本", "Time_Axis::append_time / Frequency_Trace::append_sample(int, double)", "Frequency Trace"));
|
||||
result.push_back(action("read_data_shape", "读取轨迹点数", "Frequency_Trace::sample_count / rendered_point_count / Time_Axis::time_point_count", "观察者"));
|
||||
} else if (case_id == "constellation") {
|
||||
result.push_back(action("append_points", "追加一组 IQ 点", "Constellation_Diagram::append_point", "Constellation"));
|
||||
result.push_back(action("fit_square", "按轴拟合正方形", "Constellation_Diagram::fit_square_to_axes", "Constellation"));
|
||||
result.push_back(action("read_data_shape", "读取星座点与锚点数", "Constellation_Diagram::point_count", "观察者"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const Case_Model* find_case(std::string_view id) {
|
||||
const auto& all = cases();
|
||||
const auto found = std::find_if(all.begin(), all.end(),
|
||||
@@ -173,6 +62,17 @@ const Case_Model* find_case(std::string_view id) {
|
||||
return found == all.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
std::vector<Action_Model> registered_actions(std::string_view case_id,
|
||||
Gallery_Frame_Mode frame_mode) {
|
||||
std::vector<Action_Model> result;
|
||||
append_gallery_actions<Gallery_Session_Control>(result);
|
||||
for (auto& action : gallery_frame_actions(frame_mode)) {
|
||||
result.push_back(std::move(action));
|
||||
}
|
||||
append_gallery_renderable_actions(case_id, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Json parse_request(std::string_view message) {
|
||||
return Json::parse(message.begin(), message.end());
|
||||
}
|
||||
@@ -493,7 +393,8 @@ std::string Gallery_Protocol::catalog_json() {
|
||||
gallery_renderable_control_count(item.id);
|
||||
for (const auto mode : frame_modes) {
|
||||
const std::string key(frame_mode_id(mode));
|
||||
const std::size_t actions = gallery_detail::actions(item.id, mode).size();
|
||||
const std::size_t actions =
|
||||
gallery_detail::registered_actions(item.id, mode).size();
|
||||
entry["control_count_by_mode"][key] = controls;
|
||||
entry["action_count_by_mode"][key] = actions;
|
||||
controls_total += controls;
|
||||
@@ -501,7 +402,8 @@ std::string Gallery_Protocol::catalog_json() {
|
||||
}
|
||||
entry["control_count"] = controls;
|
||||
entry["action_count"] =
|
||||
gallery_detail::actions(item.id, Gallery_Frame_Mode::Low_Latency).size();
|
||||
gallery_detail::registered_actions(
|
||||
item.id, Gallery_Frame_Mode::Low_Latency).size();
|
||||
result["cases"].push_back(std::move(entry));
|
||||
}
|
||||
result["coverage"] = {{"case_count", gallery_detail::cases().size()},
|
||||
@@ -541,9 +443,9 @@ std::string Gallery_Protocol::case_json_from_controls(
|
||||
gallery_detail::action_view())},
|
||||
{"data", Json::array()}
|
||||
};
|
||||
for (const auto& definition : gallery_detail::actions(case_id, frame_mode))
|
||||
for (const auto& action : gallery_detail::registered_actions(case_id, frame_mode))
|
||||
result["actions"]["data"].push_back(
|
||||
adminive::to_frontend_json<Json>(definition.model));
|
||||
adminive::to_frontend_json<Json>(action));
|
||||
try {
|
||||
result["telemetry"] = Json::parse(telemetry_json.begin(), telemetry_json.end());
|
||||
} catch (const std::exception&) {
|
||||
@@ -647,10 +549,10 @@ std::optional<Gallery_Control_Patch_Request> Gallery_Protocol::control_patch_req
|
||||
bool Gallery_Protocol::action_available(std::string_view case_id,
|
||||
Gallery_Frame_Mode frame_mode,
|
||||
std::string_view action_id) {
|
||||
const auto available = gallery_detail::actions(case_id, frame_mode);
|
||||
const auto available = gallery_detail::registered_actions(case_id, frame_mode);
|
||||
return std::any_of(available.begin(), available.end(),
|
||||
[action_id](const gallery_detail::Action_Definition& item) {
|
||||
return item.model.id == action_id;
|
||||
[action_id](const gallery_detail::Action_Model& item) {
|
||||
return item.id == action_id;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "Gallery_Actions.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
namespace renderive::web {
|
||||
enum class Gallery_Frame_Mode : std::uint8_t {
|
||||
Manual,
|
||||
Low_Latency,
|
||||
Playback
|
||||
};
|
||||
struct Gallery_Open_Request {
|
||||
std::string case_id;
|
||||
Gallery_Frame_Mode frame_mode = Gallery_Frame_Mode::Low_Latency;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Gallery_Actions.h"
|
||||
#include "Gallery_Renderable_Types.h"
|
||||
|
||||
#include "adminive/adminive.hpp"
|
||||
@@ -538,6 +539,112 @@ RENDERIVE_GALLERY_DESCRIPTOR(Gallery_Constellation_Diagram, Constellation_Diagra
|
||||
|
||||
} // namespace adminive
|
||||
|
||||
namespace renderive::web::gallery_action_registry {
|
||||
|
||||
constexpr auto axis_metadata() {
|
||||
return structive::type_metadata(
|
||||
gallery_action("axis_probe", "读取坐标映射与刻度",
|
||||
"coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
|
||||
"Axis"));
|
||||
}
|
||||
|
||||
} // namespace renderive::web::gallery_action_registry
|
||||
|
||||
namespace structive {
|
||||
|
||||
#define RENDERIVE_TYPE_ACTIONS(Type, ...) \
|
||||
template <> struct Type_Descriptor<renderive::web::Type> { \
|
||||
static auto get() { \
|
||||
using T = renderive::web::Type; \
|
||||
using renderive::web::gallery_action; \
|
||||
return object<T>(type_metadata(__VA_ARGS__)); \
|
||||
} \
|
||||
}
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::web::Gallery_Axis> {
|
||||
static auto get() {
|
||||
return object<renderive::web::Gallery_Axis>(
|
||||
renderive::web::gallery_action_registry::axis_metadata());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::web::Gallery_Frequency_Axis> {
|
||||
static auto get() {
|
||||
return object<renderive::web::Gallery_Frequency_Axis>(
|
||||
renderive::web::gallery_action_registry::axis_metadata());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::web::Gallery_Time_Axis> {
|
||||
static auto get() {
|
||||
using T = renderive::web::Gallery_Time_Axis;
|
||||
using renderive::web::gallery_action;
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("axis_probe", "读取坐标映射与刻度",
|
||||
"coord_to_pixel / pixel_to_coord / tick_step / sub_tick_count / tick_label",
|
||||
"Axis"),
|
||||
gallery_action("append_time", "追加时间点",
|
||||
"Time_Axis::append_time / tick_to_time", "Time_Axis")));
|
||||
}
|
||||
};
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Spectrum,
|
||||
gallery_action("push_samples", "推送一帧样本", "Spectrum::update_samples", "Spectrum"),
|
||||
gallery_action("power_at", "查询指定频率功率", "Spectrum::power_at", "Spectrum",
|
||||
{}, "number", "频率", 98e6),
|
||||
gallery_action("add_marker", "添加点 Marker", "Spectrum::add_custom_marker", "Marker",
|
||||
{}, "number", "频率", 96e6),
|
||||
gallery_action("add_line_marker", "添加线 Marker", "Spectrum::add_custom_line_marker", "Marker",
|
||||
{}, "number", "频率", 100e6),
|
||||
gallery_action("remove_marker", "按频率删除 Marker", "Spectrum::remove_custom_marker", "Marker",
|
||||
{}, "number", "频率", 96e6),
|
||||
gallery_action("remove_selected_marker", "删除选中 Marker",
|
||||
"Spectrum::remove_selected_marker", "Marker"),
|
||||
gallery_action("clear_markers", "清空 Marker", "Spectrum::clear_custom_markers", "Marker"),
|
||||
gallery_action("select_marker", "选择 Marker 索引", "Spectrum::set_selected_marker_index", "Marker",
|
||||
{}, "number", "索引", 0.0),
|
||||
gallery_action("select_next_marker", "选择下一个 Marker", "Spectrum::select_next_marker", "Marker"),
|
||||
gallery_action("select_previous_marker", "选择上一个 Marker", "Spectrum::select_previous_marker", "Marker"),
|
||||
gallery_action("clear_marker_selection", "清除 Marker 选择",
|
||||
"Spectrum::clear_marker_selection", "Marker"),
|
||||
gallery_action("set_marker_frequency", "修改选中 Marker 频率",
|
||||
"Spectrum::set_marker_frequency / set_current_marker_frequency", "Marker",
|
||||
{}, "number", "频率", 99e6));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Waterfall,
|
||||
gallery_action("append_row", "追加一行", "Waterfall::append_row", "Waterfall"),
|
||||
gallery_action("append_tick_row", "按 tick 追加一行",
|
||||
"Time_Axis::append_time / Waterfall::append_row(int, span)", "Waterfall"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Afterglow,
|
||||
gallery_action("append_spectrum", "追加一帧频谱", "Afterglow::append_spectrum", "Afterglow"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Sweep_Spectrum,
|
||||
gallery_action("append_block", "追加一个扫频块", "Sweep_Spectrum::append_block", "Sweep Spectrum"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Frequency_Trace,
|
||||
gallery_action("append_sample", "追加一个样本", "Frequency_Trace::append_sample", "Frequency Trace"),
|
||||
gallery_action("append_tick_sample", "按 tick 追加样本",
|
||||
"Time_Axis::append_time / Frequency_Trace::append_sample(int, double)",
|
||||
"Frequency Trace"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Selection_Rectangle_Overlay,
|
||||
gallery_action("clear_selection", "清空框选区域",
|
||||
"Selection_Rectangle_Overlay::clear_selected_regions", "Selection Overlay"));
|
||||
|
||||
RENDERIVE_TYPE_ACTIONS(Gallery_Constellation_Diagram,
|
||||
gallery_action("append_points", "追加一组 IQ 点",
|
||||
"Constellation_Diagram::append_point", "Constellation"),
|
||||
gallery_action("fit_square", "按轴拟合正方形",
|
||||
"Constellation_Diagram::fit_square_to_axes", "Constellation"));
|
||||
|
||||
#undef RENDERIVE_TYPE_ACTIONS
|
||||
|
||||
} // namespace structive
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
template <class Object>
|
||||
@@ -557,28 +664,51 @@ std::size_t gallery_control_count() {
|
||||
return count_fields(count_fields, descriptor.at("fields"));
|
||||
}
|
||||
|
||||
struct Gallery_Case_Type_Registration {
|
||||
std::string_view case_id;
|
||||
std::size_t (*control_count)();
|
||||
void (*append_actions)(std::vector<Gallery_Action_Model>&);
|
||||
};
|
||||
|
||||
template <class... Objects>
|
||||
Gallery_Case_Type_Registration gallery_case_types(std::string_view case_id) {
|
||||
return {
|
||||
case_id,
|
||||
[] { return (std::size_t{} + ... + gallery_control_count<Objects>()); },
|
||||
[](std::vector<Gallery_Action_Model>& actions) {
|
||||
(append_gallery_actions<Objects>(actions), ...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline const Gallery_Case_Type_Registration* gallery_case_registration(
|
||||
std::string_view case_id) {
|
||||
static const std::array registrations{
|
||||
gallery_case_types<Gallery_Frequency_Axis, Gallery_Time_Axis>("axis_lab"),
|
||||
gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Spectrum>("spectrum"),
|
||||
gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Afterglow>("afterglow"),
|
||||
gallery_case_types<Gallery_Axis, Gallery_Axis, Gallery_Sweep_Spectrum>("sweep_spectrum"),
|
||||
gallery_case_types<Gallery_Frequency_Axis, Gallery_Time_Axis, Gallery_Waterfall>("waterfall"),
|
||||
gallery_case_types<Gallery_Time_Axis, Gallery_Axis, Gallery_Frequency_Trace>("frequency_trace"),
|
||||
gallery_case_types<Gallery_Frequency_Axis, Gallery_Axis, Gallery_Spectrum,
|
||||
Gallery_Selection_Rectangle_Overlay>("selection_overlay"),
|
||||
gallery_case_types<Gallery_Axis, Gallery_Axis, Gallery_Constellation_Diagram>("constellation")
|
||||
};
|
||||
const auto found = std::find_if(
|
||||
registrations.begin(), registrations.end(),
|
||||
[case_id](const auto& registration) { return registration.case_id == case_id; });
|
||||
return found == registrations.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
inline std::size_t gallery_renderable_control_count(std::string_view case_id) {
|
||||
const auto axis = gallery_control_count<Gallery_Axis>();
|
||||
const auto frequency_axis = gallery_control_count<Gallery_Frequency_Axis>();
|
||||
const auto time_axis = gallery_control_count<Gallery_Time_Axis>();
|
||||
if (case_id == "axis_lab")
|
||||
return frequency_axis + time_axis;
|
||||
if (case_id == "spectrum")
|
||||
return frequency_axis + axis + gallery_control_count<Gallery_Spectrum>();
|
||||
if (case_id == "selection_overlay")
|
||||
return frequency_axis + axis + gallery_control_count<Gallery_Spectrum>() +
|
||||
gallery_control_count<Gallery_Selection_Rectangle_Overlay>();
|
||||
if (case_id == "waterfall")
|
||||
return frequency_axis + time_axis + gallery_control_count<Gallery_Waterfall>();
|
||||
if (case_id == "afterglow")
|
||||
return frequency_axis + axis + gallery_control_count<Gallery_Afterglow>();
|
||||
if (case_id == "sweep_spectrum")
|
||||
return 2 * axis + gallery_control_count<Gallery_Sweep_Spectrum>();
|
||||
if (case_id == "frequency_trace")
|
||||
return time_axis + axis + gallery_control_count<Gallery_Frequency_Trace>();
|
||||
if (case_id == "constellation")
|
||||
return 2 * axis + gallery_control_count<Gallery_Constellation_Diagram>();
|
||||
return 0;
|
||||
const auto* registration = gallery_case_registration(case_id);
|
||||
return registration ? registration->control_count() : 0;
|
||||
}
|
||||
|
||||
inline void append_gallery_renderable_actions(
|
||||
std::string_view case_id, std::vector<Gallery_Action_Model>& actions) {
|
||||
if (const auto* registration = gallery_case_registration(case_id))
|
||||
registration->append_actions(actions);
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -120,3 +120,21 @@ static_assert(Described_Type<renderive::web::Gallery_Session_Control>);
|
||||
static_assert(Direct_Described_Object<renderive::web::Gallery_Session_Control>);
|
||||
|
||||
} // namespace adminive
|
||||
|
||||
namespace structive {
|
||||
|
||||
template <>
|
||||
struct Type_Descriptor<renderive::web::Gallery_Session_Control> {
|
||||
static auto get() {
|
||||
using T = renderive::web::Gallery_Session_Control;
|
||||
using renderive::web::gallery_action;
|
||||
return object<T>(type_metadata(
|
||||
gallery_action("reset", "恢复本图默认值", "Gallery_Scene::rebuild", "Session"),
|
||||
gallery_action("toggle_view", "切换 View 生命周期",
|
||||
"Plot_Core::activate_view / deactivate_view", "Session"),
|
||||
gallery_action("read_data_shape", "读取数据形状",
|
||||
"Renderable data query / observer telemetry", "Observer")));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace structive
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "../app/Gallery_Plot_Session.h"
|
||||
#include "../app/Gallery_Protocol.h"
|
||||
#include "../app/Gallery_Renderables.h"
|
||||
#include "../app/Pixel_Frame.h"
|
||||
#include "../app/Web_Event_Adapter.h"
|
||||
#include "../app/Web_Plot_Session.h"
|
||||
@@ -267,6 +268,27 @@ TEST(RenderiveWebGallery, CatalogOwnsDashboardFieldsAndDynamicBehavior) {
|
||||
EXPECT_TRUE(action->at("request_frame"));
|
||||
}
|
||||
|
||||
TEST(RenderiveWebGallery, ActionsComeFromStructiveTypeMetadata) {
|
||||
using Spectrum_Schema = structive::type_descriptor_schema_t<Gallery_Spectrum>;
|
||||
using Time_Axis_Schema = structive::type_descriptor_schema_t<Gallery_Time_Axis>;
|
||||
using Manual_Strategy =
|
||||
Manual_Refresh_Strategy<Scene2D_Frame_Data, std::mutex, Observer_State<>>;
|
||||
using Manual_Schema = structive::type_descriptor_schema_t<Manual_Strategy>;
|
||||
static_assert(Spectrum_Schema::template type_attribute_count<
|
||||
Gallery_Action_Category> == 12);
|
||||
static_assert(Time_Axis_Schema::template type_attribute_count<
|
||||
Gallery_Action_Category> == 2);
|
||||
static_assert(Manual_Schema::template type_attribute_count<
|
||||
Gallery_Action_Category> == 5);
|
||||
|
||||
std::vector<Gallery_Action_Model> actions;
|
||||
append_gallery_actions<Gallery_Spectrum>(actions);
|
||||
EXPECT_EQ(actions.size(), 12U);
|
||||
EXPECT_TRUE(std::any_of(actions.begin(), actions.end(), [](const auto& action) {
|
||||
return action.id == "power_at" && action.argument_input == "number";
|
||||
}));
|
||||
}
|
||||
|
||||
TEST(RenderiveWebBridge, ComposesAdditionalEventDecoders) {
|
||||
const auto event = Extended_Web_Event_Adapter::decode(
|
||||
R"({"category":"event","type":"spatial_test","layer":7})");
|
||||
|
||||
Reference in New Issue
Block a user