RAVENNAKIT
Loading...
Searching...
No Matches
nmos_node.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2/*
3 * Project: RAVENNAKIT (RAVENNA / AES67 / ST2110-30 SDK)
4 * Copyright (c) 2024-2025 Sound on Digital
5 *
6 * This file is part of RAVENNAKIT.
7 *
8 * RAVENNAKIT is dual-licensed:
9 * 1) Under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version (the "AGPL License"); and
12 * 2) Under a commercial license from Sound on Digital, for customers who
13 * cannot (or do not wish to) comply with the AGPL License terms.
14 *
15 * If you obtained this file under the AGPL License, you may redistribute it
16 * and/or modify it under the terms of the AGPL License. See the LICENSE
17 * file in the project root for details.
18 *
19 * For commercial licensing, support, and other inquiries, please visit:
20 *
21 * https://ravennakit.com
22 *
23 */
24
25#pragma once
26
28#include "detail/nmos_error.hpp"
34#include "models/nmos_self.hpp"
42
43#include <boost/uuid.hpp>
44#include <boost/system/result.hpp>
45#include <fmt/ostream.h>
46
47#include <unordered_map>
48
49namespace rav::nmos {
50
56 public:
57 static std::array<ApiVersion, 2> k_node_api_versions;
58 static std::array<ApiVersion, 2> k_connection_api_versions;
59 static constexpr auto k_default_timeout = std::chrono::milliseconds(2000);
60 static constexpr auto k_internal_clock_name = "clk0";
61 static constexpr auto k_ptp_clock_name = "clk1";
62
67 boost::uuids::uuid id; // The UUID of the NMOS node.
70 std::string registry_address; // For when operation_mode is registered and discover_mode is manual.
71 bool enabled {false}; // Whether the node is enabled or not.
72 uint16_t api_port {0}; // The port of the local APIs (the node and connection apis).
73 std::string label; // Freeform string label for the resource.
74 std::string description; // Detailed description of the resource.
75
80 [[nodiscard]] boost::system::result<void, Error> validate() const;
81
85 [[nodiscard]] boost::json::object to_json() const;
86
92 static boost::system::result<Configuration, std::string> from_json(const boost::json::value& json);
93
94 [[nodiscard]] auto constexpr tie() const {
96 }
97
98 friend bool operator==(const Configuration& lhs, const Configuration& rhs) {
99 return lhs.tie() == rhs.tie();
100 }
101
102 friend bool operator!=(const Configuration& lhs, const Configuration& rhs) {
103 return lhs.tie() != rhs.tie();
104 }
105 };
106
107 struct StatusInfo {
108 std::string name;
109 std::string address;
110 uint16_t api_port {};
111 };
112
114
115 SafeFunction<void(const Status& status, const StatusInfo& status_info)> on_status_changed;
117
118 explicit Node(
119 boost::asio::io_context& io_context, ptp::Instance& ptp_instance, std::unique_ptr<RegistryBrowserBase> registry_browser = nullptr,
120 std::unique_ptr<HttpClientBase> http_client = nullptr
121 );
122
123 ~Node() override;
124
129 [[nodiscard]] boost::system::result<void, Error> start();
130
134 void stop();
135
141 [[nodiscard]] tl::expected<void, Error> set_configuration(Configuration new_configuration, bool force_update = false);
142
146 [[nodiscard]] const Configuration& get_configuration() const;
147
151 [[nodiscard]] boost::asio::ip::tcp::endpoint get_local_endpoint() const;
152
158 [[nodiscard]] bool add_or_update_device(Device* device);
159
165 [[nodiscard]] const Device* find_device(const boost::uuids::uuid& uuid) const;
166
172 [[nodiscard]] bool remove_device(Device* device);
173
179 [[nodiscard]] bool add_or_update_flow(FlowAudioRaw* flow);
180
186 [[nodiscard]] const FlowAudioRaw* find_flow(const boost::uuids::uuid& uuid) const;
187
193 [[nodiscard]] bool remove_flow(FlowAudioRaw* flow);
194
200 [[nodiscard]] bool add_or_update_receiver(ReceiverAudio* receiver);
201
207 [[nodiscard]] const ReceiverAudio* find_receiver(const boost::uuids::uuid& uuid) const;
208
214 [[nodiscard]] bool remove_receiver(ReceiverAudio* receiver);
215
221 [[nodiscard]] bool add_or_update_sender(Sender* sender);
222
228 [[nodiscard]] Sender* find_sender(const boost::uuids::uuid& uuid) const;
229
235 [[nodiscard]] bool remove_sender(Sender* sender);
236
242 [[nodiscard]] bool add_or_update_source(SourceAudio* source);
243
249 [[nodiscard]] const SourceAudio* find_source(const boost::uuids::uuid& uuid) const;
250
256 [[nodiscard]] bool remove_source(SourceAudio* source);
257
261 [[nodiscard]] const boost::uuids::uuid& get_uuid() const;
262
266 [[nodiscard]] const std::vector<Device*>& get_devices() const;
267
271 [[nodiscard]] const std::vector<FlowAudioRaw*>& get_flows() const;
272
276 [[nodiscard]] const std::vector<ReceiverAudio*>& get_receivers() const;
277
281 [[nodiscard]] const std::vector<Sender*>& get_senders() const;
282
286 [[nodiscard]] const std::vector<SourceAudio*>& get_sources() const;
287
291 [[nodiscard]] const Status& get_status() const;
292
296 [[nodiscard]] const StatusInfo& get_registry_info() const;
297
303
308 [[nodiscard]] static std::optional<size_t> index_of_supported_api_version(const ApiVersion& version);
309
310 void ptp_parent_changed(const ptp::ParentDs& parent) override;
311 void ptp_port_changed_state(const ptp::Port& port) override;
312
313 private:
314 static constexpr uint8_t k_max_failed_heartbeats = 5;
315 static constexpr auto k_heartbeat_interval = std::chrono::seconds(5);
316 static constexpr size_t k_clock_ptp_index = 1;
317
318 ptp::Instance& ptp_instance_;
319 Self self_;
320 std::vector<Device*> devices_;
321 std::vector<FlowAudioRaw*> flows_;
322 std::vector<ReceiverAudio*> receivers_;
323 std::vector<Sender*> senders_;
324 std::vector<SourceAudio*> sources_;
325
326 Configuration configuration_;
327 Status status_ {Status::disabled};
328 int post_resource_error_count_ = 0;
329 NetworkInterfaceConfig network_interface_config_;
330
331 std::optional<dnssd::ServiceDescription> selected_registry_;
332 StatusInfo status_info_;
333
334 HttpServer http_server_;
335 std::unique_ptr<HttpClientBase> http_client_;
336 std::unique_ptr<RegistryBrowserBase> registry_browser_;
337 AsioTimer timer_;
338
339 uint8_t failed_heartbeat_count_ = 0;
340 AsioTimer heartbeat_timer_; // Keep below connector_ to avoid dangling reference
341 Version current_version_;
342
343 [[nodiscard]] boost::system::result<void, Error> start_internal();
344 void stop_internal();
345
346 void register_async();
347 void unregister_async();
348 void post_resource_async(std::string type, boost::json::value resource);
349 void delete_resource_async(std::string resource_type, const boost::uuids::uuid& id);
350 void update_self();
351 void send_heartbeat_async();
352 void connect_to_registry_async();
353 void connect_to_registry_async(std::string_view host, std::string_view service);
354
355 [[nodiscard]] bool add_receiver_to_device(const ReceiverAudio& receiver) const;
356 [[nodiscard]] bool add_sender_to_device(const Sender& sender) const;
357
358 bool select_registry(const dnssd::ServiceDescription& desc);
359 void handle_registry_discovered(const dnssd::ServiceDescription& desc);
360
361 void set_status(Status new_status);
362 void update_all_resources_to_now();
363 void send_updated_resources_async();
364 void update_device(Device& device);
365};
366
367const char* to_string(const Node::Status& status);
368
369void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, const Node::Configuration& config);
370
371} // namespace rav::nmos
A timer that uses boost::asio::steady_timer to provide a simple way of creating a timer without think...
A high-level wrapper around boost::beast for creating an HTTP server.
A simple callback function wrapper that can be used to store and call a function with a specific sign...
const std::vector< SourceAudio * > & get_sources() const
static std::array< ApiVersion, 2 > k_connection_api_versions
Definition nmos_node.hpp:58
bool remove_sender(Sender *sender)
Removes a sender from the node by its uuid.
void set_network_interface_config(NetworkInterfaceConfig config)
Updates the node based on given network interface configuration.
const std::vector< ReceiverAudio * > & get_receivers() const
static constexpr auto k_ptp_clock_name
Definition nmos_node.hpp:61
Node(boost::asio::io_context &io_context, ptp::Instance &ptp_instance, std::unique_ptr< RegistryBrowserBase > registry_browser=nullptr, std::unique_ptr< HttpClientBase > http_client=nullptr)
bool remove_source(SourceAudio *source)
Removes a source from the node by its uuid.
bool add_or_update_sender(Sender *sender)
Adds the given sender to the node or updates an existing sender if it already exists (based on the uu...
static std::array< ApiVersion, 2 > k_node_api_versions
Definition nmos_node.hpp:57
bool add_or_update_device(Device *device)
Adds the given device to the node or updates an existing device if it already exists (based on the uu...
const std::vector< Sender * > & get_senders() const
const FlowAudioRaw * find_flow(const boost::uuids::uuid &uuid) const
Finds a flow by its uuid.
const Configuration & get_configuration() const
const std::vector< FlowAudioRaw * > & get_flows() const
const Device * find_device(const boost::uuids::uuid &uuid) const
Finds a device by its uuid.
bool remove_flow(FlowAudioRaw *flow)
Removes a flow from the node by its uuid.
void ptp_parent_changed(const ptp::ParentDs &parent) override
Called when the parent of the PTP instance changes.
const ReceiverAudio * find_receiver(const boost::uuids::uuid &uuid) const
Finds a receiver by its uuid.
const Status & get_status() const
~Node() override
static constexpr auto k_default_timeout
Definition nmos_node.hpp:59
SafeFunction< void(const Status &status, const StatusInfo &status_info)> on_status_changed
const boost::uuids::uuid & get_uuid() const
const StatusInfo & get_registry_info() const
bool add_or_update_receiver(ReceiverAudio *receiver)
Adds the given receiver to the node or updates an existing receiver if it already exists (based on th...
boost::asio::ip::tcp::endpoint get_local_endpoint() const
bool add_or_update_source(SourceAudio *source)
Adds the given source to the node or updates an existing source if it already exists (based on the uu...
void ptp_port_changed_state(const ptp::Port &port) override
Called when the state of a port changes.
const SourceAudio * find_source(const boost::uuids::uuid &uuid) const
Finds a source by its uuid.
bool remove_device(Device *device)
Removes a device from the node by its uuid.
Sender * find_sender(const boost::uuids::uuid &uuid) const
Finds a sender by its uuid.
SafeFunction< void(const Configuration &config)> on_configuration_changed
static std::optional< size_t > index_of_supported_api_version(const ApiVersion &version)
static constexpr auto k_internal_clock_name
Definition nmos_node.hpp:60
void stop()
Stops all the operations of this node (HTTP server, advertisements, etc.).
const std::vector< Device * > & get_devices() const
bool remove_receiver(ReceiverAudio *receiver)
Removes a receiver from the node by its uuid.
tl::expected< void, Error > set_configuration(Configuration new_configuration, bool force_update=false)
Sets the configuration of the NMOS node.
bool add_or_update_flow(FlowAudioRaw *flow)
Adds the given flow to the node or updates an existing flow if it already exists (based on the uuid).
boost::system::result< void, Error > start()
Starts the services of this node (HTTP server, advertisements, etc.).
Represents a PTP instance as defined in IEEE 1588-2019.
void tag_invoke(const boost::json::value_from_tag &, boost::json::value &jv, const Timestamp &timestamp)
const char * to_string(const OperationMode operation_mode)
Timestamp Version
An nmos version is represented as TAI timestamp.
OperationMode
The mode of operation for the NMOS node.
@ mdns_p2p
Discovers registries via mDNS, falling back to p2p if no registry is available.
A struct containing data which represents a service on the network.
Represents the version of the NMOS API.
The configuration of the NMOS node.
Definition nmos_node.hpp:66
static boost::system::result< Configuration, std::string > from_json(const boost::json::value &json)
Creates a configuration object from a JSON object.
auto constexpr tie() const
Definition nmos_node.hpp:94
friend bool operator!=(const Configuration &lhs, const Configuration &rhs)
boost::json::object to_json() const
boost::system::result< void, Error > validate() const
Checks if the configuration is semantically valid, return a message if not.
friend bool operator==(const Configuration &lhs, const Configuration &rhs)
Definition nmos_node.hpp:98
Describes an audio Receiver.
Describes the Node and the services which run on it.
Definition nmos_self.hpp:36
Describes a sender.
Describes an audio source.
Represents the parent data set as described in IEEE1588-2019: 8.2.3.