4b50f49172
- node_runner: track all readers/writers in flow_resources vector, release them via mxlReleaseFlowWriter/mxlReleaseFlowReader before destroying the MXL instance (fixes 'leaked flow writer' warning) - node_runner: remove_writer/remove_reader commands now also release the MXL flow resources, not just reset the node's optional<> - engine: call process_manager.stop_all(graph) on shutdown to kill child node processes (prevents orphaned passthrough processes) - graph: add get_node_mut() for process_manager to update node state - process_manager: implement stop_all(Graph&) that SIGTERMs all running node processes - passthrough: reduce logging to first-grain and realign-once only - tests: update flow format assertion to match NMOS (urn:x-nmos:format:video instead of video/v210)
146 lines
4.1 KiB
C++
146 lines
4.1 KiB
C++
#include <dmf-engine/graph.hpp>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include <spdlog/spdlog.h>
|
|
|
|
namespace dmf_engine {
|
|
|
|
NodeId Graph::add_node(const std::string& type, const nlohmann::json& config) {
|
|
std::string id;
|
|
if (config.contains("id") && config["id"].is_string()) {
|
|
id = config["id"].get<std::string>();
|
|
} else {
|
|
id = type + "_" + std::to_string(next_node_num_++);
|
|
}
|
|
GraphNode node;
|
|
node.id = id;
|
|
node.type = type;
|
|
node.config = config;
|
|
node.state = NodeState::Stopped;
|
|
nodes_[id] = std::move(node);
|
|
spdlog::info("Graph: added node '{}' type='{}'", id, type);
|
|
return id;
|
|
}
|
|
|
|
bool Graph::remove_node(const NodeId& node_id) {
|
|
auto it = nodes_.find(node_id);
|
|
if (it == nodes_.end()) {
|
|
spdlog::warn("Graph: node '{}' not found", node_id);
|
|
return false;
|
|
}
|
|
|
|
std::vector<EdgeId> edges_to_remove;
|
|
for (const auto& [eid, edge] : edges_) {
|
|
if (edge.from_node == node_id || edge.to_node == node_id) {
|
|
edges_to_remove.push_back(eid);
|
|
}
|
|
}
|
|
for (const auto& eid : edges_to_remove) {
|
|
edges_.erase(eid);
|
|
spdlog::info("Graph: removed edge '{}' (connected to removed node '{}')", eid, node_id);
|
|
}
|
|
|
|
nodes_.erase(it);
|
|
spdlog::info("Graph: removed node '{}'", node_id);
|
|
return true;
|
|
}
|
|
|
|
const GraphNode* Graph::get_node(const NodeId& node_id) const {
|
|
auto it = nodes_.find(node_id);
|
|
return it != nodes_.end() ? &it->second : nullptr;
|
|
}
|
|
|
|
GraphNode* Graph::get_node_mut(const NodeId& node_id) {
|
|
auto it = nodes_.find(node_id);
|
|
return it != nodes_.end() ? &it->second : nullptr;
|
|
}
|
|
|
|
std::vector<GraphNode> Graph::get_nodes() const {
|
|
std::vector<GraphNode> result;
|
|
for (const auto& [_, node] : nodes_) {
|
|
result.push_back(node);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
EdgeId Graph::add_edge(const NodeId& from_node, const PortId& from_port,
|
|
const NodeId& to_node, const PortId& to_port,
|
|
const FlowId& flow_id, const nlohmann::json& flow_def) {
|
|
auto id = from_node + ":" + from_port + "->" + to_node + ":" + to_port;
|
|
GraphEdge edge;
|
|
edge.id = id;
|
|
edge.from_node = from_node;
|
|
edge.from_port = from_port;
|
|
edge.to_node = to_node;
|
|
edge.to_port = to_port;
|
|
edge.flow_id = flow_id;
|
|
edge.flow_def = flow_def;
|
|
edges_[id] = std::move(edge);
|
|
spdlog::info("Graph: added edge '{}' flow_id={}", id, flow_id);
|
|
return id;
|
|
}
|
|
|
|
bool Graph::remove_edge(const EdgeId& edge_id) {
|
|
auto it = edges_.find(edge_id);
|
|
if (it == edges_.end()) {
|
|
spdlog::warn("Graph: edge '{}' not found", edge_id);
|
|
return false;
|
|
}
|
|
edges_.erase(it);
|
|
spdlog::info("Graph: removed edge '{}'", edge_id);
|
|
return true;
|
|
}
|
|
|
|
std::vector<GraphEdge> Graph::get_edges() const {
|
|
std::vector<GraphEdge> result;
|
|
for (const auto& [_, edge] : edges_) {
|
|
result.push_back(edge);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<GraphEdge> Graph::get_edges_for_node(const NodeId& node_id) const {
|
|
std::vector<GraphEdge> result;
|
|
for (const auto& [_, edge] : edges_) {
|
|
if (edge.from_node == node_id || edge.to_node == node_id) {
|
|
result.push_back(edge);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
nlohmann::json Graph::serialize() const {
|
|
auto j = nlohmann::json::object();
|
|
|
|
auto nodes_arr = nlohmann::json::array();
|
|
for (const auto& [id, node] : nodes_) {
|
|
nodes_arr.push_back({
|
|
{"id", node.id},
|
|
{"type", node.type},
|
|
{"config", node.config},
|
|
{"state", static_cast<int>(node.state)},
|
|
{"control_port", node.control_port},
|
|
{"pid", node.pid},
|
|
});
|
|
}
|
|
j["nodes"] = nodes_arr;
|
|
|
|
auto edges_arr = nlohmann::json::array();
|
|
for (const auto& [id, edge] : edges_) {
|
|
edges_arr.push_back({
|
|
{"id", edge.id},
|
|
{"from_node", edge.from_node},
|
|
{"from_port", edge.from_port},
|
|
{"to_node", edge.to_node},
|
|
{"to_port", edge.to_port},
|
|
{"flow_id", edge.flow_id},
|
|
});
|
|
}
|
|
j["edges"] = edges_arr;
|
|
|
|
return j;
|
|
}
|
|
|
|
} // namespace dmf_engine
|