Add debug graph to static network
This commit is contained in:
parent
011b5eb35f
commit
da8f4d9926
@ -35,6 +35,7 @@ if(OpenCV_FOUND)
|
|||||||
|
|
||||||
if(KPN_WEB_DEBUG)
|
if(KPN_WEB_DEBUG)
|
||||||
kpn_target_enable_web_debug(09_opencv_cellshade)
|
kpn_target_enable_web_debug(09_opencv_cellshade)
|
||||||
|
kpn_target_enable_web_debug(13_debug_cellshade)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "KPN++ example 09_opencv_cellshade: OpenCV ${OpenCV_VERSION} found — building")
|
message(STATUS "KPN++ example 09_opencv_cellshade: OpenCV ${OpenCV_VERSION} found — building")
|
||||||
else()
|
else()
|
||||||
|
|||||||
@ -172,4 +172,20 @@ private:
|
|||||||
ChannelStats stats_;
|
ChannelStats stats_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Channel probe — type-erased snapshot accessor ─────────────────────────────
|
||||||
|
// Used by both Network and StaticNetwork for diagnostics.
|
||||||
|
|
||||||
|
struct IChannelProbe {
|
||||||
|
virtual ~IChannelProbe() = default;
|
||||||
|
virtual ChannelSnapshot snapshot() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct ChannelProbe : IChannelProbe {
|
||||||
|
const Channel<T>& ch;
|
||||||
|
std::string name;
|
||||||
|
ChannelProbe(const Channel<T>& c, std::string n) : ch(c), name(std::move(n)) {}
|
||||||
|
ChannelSnapshot snapshot() const override { return ch.snapshot(name); }
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace kpn
|
} // namespace kpn
|
||||||
|
|||||||
@ -53,6 +53,7 @@ public:
|
|||||||
|
|
||||||
static constexpr std::size_t input_count = 1;
|
static constexpr std::size_t input_count = 1;
|
||||||
static constexpr std::size_t output_count = N;
|
static constexpr std::size_t output_count = N;
|
||||||
|
static constexpr std::size_t unique_tag = Id;
|
||||||
|
|
||||||
explicit FanoutNode(std::size_t fifo_capacity = 5)
|
explicit FanoutNode(std::size_t fifo_capacity = 5)
|
||||||
: fifo_capacity_(fifo_capacity)
|
: fifo_capacity_(fifo_capacity)
|
||||||
|
|||||||
@ -35,21 +35,6 @@ public:
|
|||||||
: std::runtime_error(std::move(msg)) {}
|
: std::runtime_error(std::move(msg)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Channel snapshot accessor — type-erased so Network can collect them ───────
|
|
||||||
|
|
||||||
struct IChannelProbe {
|
|
||||||
virtual ~IChannelProbe() = default;
|
|
||||||
virtual ChannelSnapshot snapshot() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct ChannelProbe : IChannelProbe {
|
|
||||||
const Channel<T>& ch;
|
|
||||||
std::string name;
|
|
||||||
ChannelProbe(const Channel<T>& c, std::string n) : ch(c), name(std::move(n)) {}
|
|
||||||
ChannelSnapshot snapshot() const override { return ch.snapshot(name); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── Network ───────────────────────────────────────────────────────────────────
|
// ── Network ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
class Network : public INode {
|
class Network : public INode {
|
||||||
|
|||||||
@ -7,11 +7,17 @@
|
|||||||
#include "tmp/fanout_groups.hpp"
|
#include "tmp/fanout_groups.hpp"
|
||||||
#include "tmp/topo_sort.hpp"
|
#include "tmp/topo_sort.hpp"
|
||||||
|
|
||||||
|
#ifdef KPN_WEB_DEBUG
|
||||||
|
#include "web_debug.hpp"
|
||||||
|
#include <memory>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace kpn {
|
namespace kpn {
|
||||||
|
|
||||||
@ -82,23 +88,41 @@ public:
|
|||||||
StaticNetwork(std::unique_ptr<FanoutStorage> fanouts,
|
StaticNetwork(std::unique_ptr<FanoutStorage> fanouts,
|
||||||
std::vector<INode*> user_nodes_topo,
|
std::vector<INode*> user_nodes_topo,
|
||||||
std::vector<INode*> fanout_ptrs,
|
std::vector<INode*> fanout_ptrs,
|
||||||
std::vector<std::string> user_node_names)
|
std::vector<std::string> user_node_names,
|
||||||
|
std::vector<std::string> fanout_node_names,
|
||||||
|
std::vector<std::unique_ptr<IChannelProbe>> channel_probes)
|
||||||
: fanouts_(std::move(fanouts))
|
: fanouts_(std::move(fanouts))
|
||||||
, user_nodes_topo_(std::move(user_nodes_topo))
|
, user_nodes_topo_(std::move(user_nodes_topo))
|
||||||
, fanout_nodes_ptr_(std::move(fanout_ptrs))
|
, fanout_nodes_ptr_(std::move(fanout_ptrs))
|
||||||
, user_node_names_(std::move(user_node_names))
|
, user_node_names_(std::move(user_node_names))
|
||||||
|
, fanout_node_names_(std::move(fanout_node_names))
|
||||||
|
, channel_probes_(std::move(channel_probes))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~StaticNetwork() override { stop(); }
|
~StaticNetwork() override { stop(); }
|
||||||
|
|
||||||
void start() override {
|
void start() override {
|
||||||
stop_flag_ = false;
|
stop_flag_ = false;
|
||||||
|
start_time_ = clock_t::now();
|
||||||
for (auto* n : user_nodes_topo_) n->start();
|
for (auto* n : user_nodes_topo_) n->start();
|
||||||
for (auto* n : fanout_nodes_ptr_) n->start();
|
for (auto* n : fanout_nodes_ptr_) n->start();
|
||||||
|
#ifdef KPN_WEB_DEBUG
|
||||||
|
web_server_ = std::make_unique<web_debug::WebDebugServer>(
|
||||||
|
web_debug_port_,
|
||||||
|
[this]() {
|
||||||
|
auto s = collect_snapshots();
|
||||||
|
return web_debug::to_json(s.nodes, s.channels, s.elapsed_s);
|
||||||
|
});
|
||||||
|
web_server_->start();
|
||||||
|
std::cerr << "[kpn] web debug UI: http://localhost:" << web_debug_port_ << "\n";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop() override {
|
void stop() override {
|
||||||
stop_flag_ = true;
|
stop_flag_ = true;
|
||||||
|
#ifdef KPN_WEB_DEBUG
|
||||||
|
if (web_server_) web_server_->stop();
|
||||||
|
#endif
|
||||||
for (auto it = fanout_nodes_ptr_.rbegin(); it != fanout_nodes_ptr_.rend(); ++it)
|
for (auto it = fanout_nodes_ptr_.rbegin(); it != fanout_nodes_ptr_.rend(); ++it)
|
||||||
(*it)->stop();
|
(*it)->stop();
|
||||||
for (auto it = user_nodes_topo_.rbegin(); it != user_nodes_topo_.rend(); ++it)
|
for (auto it = user_nodes_topo_.rbegin(); it != user_nodes_topo_.rend(); ++it)
|
||||||
@ -113,6 +137,10 @@ public:
|
|||||||
return {n, 0, 0, 0, 0, 0, 0, 0};
|
return {n, 0, 0, 0, 0, 0, 0, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef KPN_WEB_DEBUG
|
||||||
|
void set_web_debug_port(uint16_t port) { web_debug_port_ = port; }
|
||||||
|
#endif
|
||||||
|
|
||||||
// Print diagnostics using compile-time node labels
|
// Print diagnostics using compile-time node labels
|
||||||
void print_diagnostics(std::ostream& os = std::cerr) const {
|
void print_diagnostics(std::ostream& os = std::cerr) const {
|
||||||
os << "\n┌─ KPN++ StaticNetwork diagnostics ─────────────────────────────\n";
|
os << "\n┌─ KPN++ StaticNetwork diagnostics ─────────────────────────────\n";
|
||||||
@ -128,12 +156,42 @@ public:
|
|||||||
FanoutStorage& fanouts_storage() { return *fanouts_; }
|
FanoutStorage& fanouts_storage() { return *fanouts_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Snapshots {
|
||||||
|
std::vector<NodeSnapshot> nodes;
|
||||||
|
std::vector<ChannelSnapshot> channels;
|
||||||
|
double elapsed_s;
|
||||||
|
};
|
||||||
|
|
||||||
|
Snapshots collect_snapshots() const {
|
||||||
|
double elapsed_s = std::chrono::duration<double>(
|
||||||
|
clock_t::now() - start_time_).count();
|
||||||
|
|
||||||
|
std::vector<NodeSnapshot> nodes;
|
||||||
|
for (std::size_t i = 0; i < user_nodes_topo_.size(); ++i)
|
||||||
|
nodes.push_back(user_nodes_topo_[i]->node_snapshot(user_node_names_[i], elapsed_s));
|
||||||
|
for (std::size_t i = 0; i < fanout_nodes_ptr_.size(); ++i)
|
||||||
|
nodes.push_back(fanout_nodes_ptr_[i]->node_snapshot(fanout_node_names_[i], elapsed_s));
|
||||||
|
|
||||||
|
std::vector<ChannelSnapshot> channels;
|
||||||
|
for (auto& probe : channel_probes_)
|
||||||
|
channels.push_back(probe->snapshot());
|
||||||
|
|
||||||
|
return {std::move(nodes), std::move(channels), elapsed_s};
|
||||||
|
}
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
bool stop_flag_{false};
|
bool stop_flag_{false};
|
||||||
std::unique_ptr<FanoutStorage> fanouts_;
|
std::unique_ptr<FanoutStorage> fanouts_;
|
||||||
std::vector<INode*> user_nodes_topo_;
|
std::vector<INode*> user_nodes_topo_;
|
||||||
std::vector<INode*> fanout_nodes_ptr_;
|
std::vector<INode*> fanout_nodes_ptr_;
|
||||||
std::vector<std::string> user_node_names_; // parallel to user_nodes_topo_
|
std::vector<std::string> user_node_names_;
|
||||||
|
std::vector<std::string> fanout_node_names_;
|
||||||
|
std::vector<std::unique_ptr<IChannelProbe>> channel_probes_;
|
||||||
|
clock_t::time_point start_time_;
|
||||||
|
#ifdef KPN_WEB_DEBUG
|
||||||
|
uint16_t web_debug_port_{9090};
|
||||||
|
std::unique_ptr<web_debug::WebDebugServer> web_server_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── make_network ──────────────────────────────────────────────────────────────
|
// ── make_network ──────────────────────────────────────────────────────────────
|
||||||
@ -203,27 +261,39 @@ auto make_network(Edges&&... edges) {
|
|||||||
return ptr;
|
return ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<IChannelProbe>> channel_probes;
|
||||||
|
|
||||||
auto wire_one = [&]<typename SE>(SE) {
|
auto wire_one = [&]<typename SE>(SE) {
|
||||||
using SrcNode = typename SE::src_node_t;
|
using SrcNode = typename SE::src_node_t;
|
||||||
using DstNode = typename SE::dst_node_t;
|
using DstNode = typename SE::dst_node_t;
|
||||||
|
using out_t = std::tuple_element_t<SE::src_idx, typename SrcNode::return_tuple>;
|
||||||
constexpr std::size_t SrcIdx = SE::src_idx;
|
constexpr std::size_t SrcIdx = SE::src_idx;
|
||||||
constexpr std::size_t DstIdx = SE::dst_idx;
|
constexpr std::size_t DstIdx = SE::dst_idx;
|
||||||
|
|
||||||
auto* src = find_node.template operator()<SrcNode>();
|
auto* src = find_node.template operator()<SrcNode>();
|
||||||
auto* dst = find_node.template operator()<DstNode>();
|
auto* dst = find_node.template operator()<DstNode>();
|
||||||
if (src && dst)
|
if (src && dst) {
|
||||||
src->template set_output_channel<SrcIdx>(
|
auto& ch = dst->template input_channel<DstIdx>();
|
||||||
&dst->template input_channel<DstIdx>());
|
src->template set_output_channel<SrcIdx>(&ch);
|
||||||
|
std::string ch_name = node_display_name<SrcNode>() + ":" + std::to_string(SrcIdx)
|
||||||
|
+ " \xe2\x86\x92 " // UTF-8 →
|
||||||
|
+ node_display_name<DstNode>() + ":" + std::to_string(DstIdx);
|
||||||
|
channel_probes.push_back(std::make_unique<ChannelProbe<out_t>>(ch, ch_name));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[&]<typename... SEs>(tmp::TypeList<SEs...>) {
|
[&]<typename... SEs>(tmp::TypeList<SEs...>) {
|
||||||
(wire_one(SEs{}), ...);
|
(wire_one(SEs{}), ...);
|
||||||
}(ExpandedEdges{});
|
}(ExpandedEdges{});
|
||||||
|
|
||||||
// 6. Collect fanout node pointers
|
// 6. Collect fanout node pointers and names
|
||||||
std::vector<INode*> fanout_ptrs;
|
std::vector<INode*> fanout_ptrs;
|
||||||
|
std::vector<std::string> fanout_node_names;
|
||||||
std::apply([&](auto&... fn) {
|
std::apply([&](auto&... fn) {
|
||||||
(fanout_ptrs.push_back(static_cast<INode*>(&fn)), ...);
|
([&](auto& node) {
|
||||||
|
fanout_ptrs.push_back(static_cast<INode*>(&node));
|
||||||
|
fanout_node_names.push_back(node_display_name<std::decay_t<decltype(node)>>());
|
||||||
|
}(fn), ...);
|
||||||
}, *fanout_storage);
|
}, *fanout_storage);
|
||||||
|
|
||||||
// 7. Construct and return the StaticNetwork
|
// 7. Construct and return the StaticNetwork
|
||||||
@ -231,7 +301,9 @@ auto make_network(Edges&&... edges) {
|
|||||||
return Net(std::move(fanout_storage),
|
return Net(std::move(fanout_storage),
|
||||||
std::move(user_node_ptrs),
|
std::move(user_node_ptrs),
|
||||||
std::move(fanout_ptrs),
|
std::move(fanout_ptrs),
|
||||||
std::move(user_node_names));
|
std::move(user_node_names),
|
||||||
|
std::move(fanout_node_names),
|
||||||
|
std::move(channel_probes));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace kpn
|
} // namespace kpn
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user