diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-05-15 10:26:44 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:14 -0800 |
commit | b8a6c64d6012ab1ec0b3b843fccec2d990d440a3 (patch) | |
tree | 31a99d71af5a6aa2db2a7c9f2a7d19986a2d3856 /host/tests | |
parent | d6251df6347390e74784b2fbe116b0e64780547e (diff) | |
download | uhd-b8a6c64d6012ab1ec0b3b843fccec2d990d440a3.tar.gz uhd-b8a6c64d6012ab1ec0b3b843fccec2d990d440a3.tar.bz2 uhd-b8a6c64d6012ab1ec0b3b843fccec2d990d440a3.zip |
rfnoc: Add action API
- Added action_info class
- Allow to send actions from node to node
- Allow to post actions into nodes
- Allow to set default forwarding policies
- Added unit tests
Diffstat (limited to 'host/tests')
-rw-r--r-- | host/tests/CMakeLists.txt | 13 | ||||
-rw-r--r-- | host/tests/actions_test.cpp | 81 | ||||
-rw-r--r-- | host/tests/rfnoc_graph_mock_nodes.hpp | 122 |
3 files changed, 214 insertions, 2 deletions
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index f31daed50..c308abdcb 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -246,6 +246,19 @@ UHD_INSTALL(TARGETS COMPONENT tests ) +add_executable(actions_test + actions_test.cpp + ${CMAKE_SOURCE_DIR}/lib/rfnoc/graph.cpp +) +target_link_libraries(actions_test uhd ${Boost_LIBRARIES}) +UHD_ADD_TEST(actions_test actions_test) +UHD_INSTALL(TARGETS + actions_test + RUNTIME + DESTINATION ${PKG_LIB_DIR}/tests + COMPONENT tests +) + ######################################################################## # demo of a loadable module ######################################################################## diff --git a/host/tests/actions_test.cpp b/host/tests/actions_test.cpp new file mode 100644 index 000000000..c0344eacf --- /dev/null +++ b/host/tests/actions_test.cpp @@ -0,0 +1,81 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/node.hpp> +#include <uhd/rfnoc/actions.hpp> +#include <uhd/utils/log.hpp> +#include <uhdlib/rfnoc/node_accessor.hpp> +#include <uhdlib/rfnoc/prop_accessor.hpp> +#include <uhdlib/rfnoc/graph.hpp> +#include <boost/test/unit_test.hpp> +#include <iostream> + +#include "rfnoc_graph_mock_nodes.hpp" + + +const std::string STREAM_CMD_KEY = "stream_cmd"; + +BOOST_AUTO_TEST_CASE(test_actions_single_node) +{ + node_accessor_t node_accessor{}; + + // Define some mock nodes: + mock_radio_node_t mock_radio(0); + + auto stream_cmd = action_info::make(STREAM_CMD_KEY); + std::string cmd_payload = "START"; + stream_cmd->payload = std::vector<uint8_t>(cmd_payload.begin(), cmd_payload.end()); + + auto other_cmd = action_info::make("FOO"); + + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, stream_cmd); + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, other_cmd); + + mock_radio.update_fwd_policy(node_t::forwarding_policy_t::ONE_TO_ONE); + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, other_cmd); + mock_radio.update_fwd_policy(node_t::forwarding_policy_t::ONE_TO_FAN); + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, other_cmd); + mock_radio.update_fwd_policy(node_t::forwarding_policy_t::ONE_TO_ALL); + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, other_cmd); + mock_radio.update_fwd_policy(node_t::forwarding_policy_t::ONE_TO_ALL_IN); + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, other_cmd); + mock_radio.update_fwd_policy(node_t::forwarding_policy_t::ONE_TO_ALL_OUT); + node_accessor.send_action(&mock_radio, {res_source_info::INPUT_EDGE, 0}, other_cmd); +} + +BOOST_AUTO_TEST_CASE(test_actions_simple_graph) +{ + node_accessor_t node_accessor{}; + uhd::rfnoc::detail::graph_t graph{}; + + // Define some mock nodes: + mock_radio_node_t mock_rx_radio{0}; + mock_ddc_node_t mock_ddc{}; + mock_fifo_t mock_fifo{1}; + mock_streamer_t mock_streamer{1}; + + // These init calls would normally be done by the framework + node_accessor.init_props(&mock_rx_radio); + node_accessor.init_props(&mock_ddc); + node_accessor.init_props(&mock_fifo); + node_accessor.init_props(&mock_streamer); + + graph.connect(&mock_rx_radio, &mock_ddc, {0, 0, graph_edge_t::DYNAMIC, true}); + graph.connect(&mock_ddc, &mock_fifo, {0, 0, graph_edge_t::DYNAMIC, true}); + graph.connect(&mock_fifo, &mock_streamer, {0, 0, graph_edge_t::DYNAMIC, true}); + graph.initialize(); + + // Force the DDC to actually set a decimation rate != 1 + mock_streamer.set_property<double>("samp_rate", 10e6, 0); + + uhd::stream_cmd_t num_samps_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); + constexpr size_t NUM_SAMPS = 100; + num_samps_cmd.num_samps = NUM_SAMPS; + + mock_streamer.issue_stream_cmd(num_samps_cmd, 0); + BOOST_CHECK_EQUAL(NUM_SAMPS * mock_ddc.get_property<int>("decim", 0), + mock_rx_radio.last_num_samps); +} diff --git a/host/tests/rfnoc_graph_mock_nodes.hpp b/host/tests/rfnoc_graph_mock_nodes.hpp index 85e667ebd..a9d8d4e55 100644 --- a/host/tests/rfnoc_graph_mock_nodes.hpp +++ b/host/tests/rfnoc_graph_mock_nodes.hpp @@ -7,7 +7,9 @@ #ifndef INCLUDED_LIBUHD_TESTS_MOCK_NODES_HPP #define INCLUDED_LIBUHD_TESTS_MOCK_NODES_HPP +#include <uhd/rfnoc/defaults.hpp> #include <uhd/rfnoc/node.hpp> +#include <uhd/types/stream_cmd.hpp> using namespace uhd::rfnoc; @@ -82,9 +84,37 @@ public: rssi_resolver_count++; _rssi = static_cast<double>(rssi_resolver_count); }); + + + set_action_forwarding_policy(forwarding_policy_t::DROP); + + register_action_handler( + "stream_cmd", [this](const res_source_info& src, action_info::sptr action) { + UHD_ASSERT_THROW(action->key == "stream_cmd"); + const std::string cmd(action->payload.begin(), action->payload.end()); + UHD_LOG_INFO(get_unique_id(), + "Received stream command: " << cmd << " to " << src.to_string()); + if (cmd == "START") { + UHD_LOG_INFO(get_unique_id(), "Starting Stream!"); + } else if (cmd == "STOP") { + UHD_LOG_INFO(get_unique_id(), "Stopping Stream!"); + } else { + this->last_num_samps = std::stoul(cmd); + UHD_LOG_INFO(get_unique_id(), + "Streaming num samps: " << this->last_num_samps); + } + }); } - std::string get_unique_id() const { return "MOCK_RADIO" + std::to_string(_radio_idx); } + void update_fwd_policy(forwarding_policy_t policy) + { + set_action_forwarding_policy(policy); + } + + std::string get_unique_id() const + { + return "MOCK_RADIO" + std::to_string(_radio_idx); + } size_t get_num_input_ports() const { @@ -101,6 +131,8 @@ public: bool disable_samp_out_resolver = false; double force_samp_out_value = 23e6; + size_t last_num_samps = 0; + private: const size_t _radio_idx; @@ -162,6 +194,31 @@ public: decim = coerce_decim(int(samp_rate_in.get() / samp_rate_out.get())); samp_rate_in = samp_rate_out.get() * decim.get(); }); + + register_action_handler( + "stream_cmd", [this](const res_source_info& src, action_info::sptr action) { + res_source_info dst_edge{ + res_source_info::invert_edge(src.type), src.instance}; + auto new_action = action_info::make(action->key); + std::string cmd(action->payload.begin(), action->payload.end()); + if (cmd == "START" || cmd == "STOP") { + new_action->payload = action->payload; + } else { + unsigned long long num_samps = std::stoull(cmd); + if (src.type == res_source_info::OUTPUT_EDGE) { + num_samps *= _decim.get(); + } else { + num_samps /= _decim.get(); + } + std::string new_cmd = std::to_string(num_samps); + new_action->payload.insert( + new_action->payload.begin(), new_cmd.begin(), new_cmd.end()); + } + + UHD_LOG_INFO(get_unique_id(), + "Forwarding stream_cmd, decim is " << _decim.get()); + post_action(dst_edge, new_action); + }); } std::string get_unique_id() const { return "MOCK_DDC"; } @@ -203,7 +260,7 @@ private: /*! FIFO * - * Not much here -- we use it to test dynamic prop forwarding. + * Not much here -- we use it to test dynamic prop and action forwarding. */ class mock_fifo_t : public node_t { @@ -211,6 +268,7 @@ public: mock_fifo_t(const size_t num_ports) : _num_ports(num_ports) { set_prop_forwarding_policy(forwarding_policy_t::ONE_TO_ONE); + set_action_forwarding_policy(forwarding_policy_t::ONE_TO_ONE); } std::string get_unique_id() const { return "MOCK_FIFO"; } @@ -230,4 +288,64 @@ private: const size_t _num_ports; }; +/*! Streamer + * + * Not much here -- we use it to test dynamic prop and action forwarding. + */ +class mock_streamer_t : public node_t +{ +public: + mock_streamer_t(const size_t num_ports) : _num_ports(num_ports) + { + set_prop_forwarding_policy(forwarding_policy_t::DROP); + set_action_forwarding_policy(forwarding_policy_t::DROP); + register_property(&_samp_rate_user); + register_property(&_samp_rate_in); + add_property_resolver({&_samp_rate_user}, {&_samp_rate_in}, [this]() { + UHD_LOG_INFO(get_unique_id(), "Calling resolver for `samp_rate_user'..."); + _samp_rate_in = _samp_rate_user.get(); + }); + add_property_resolver({&_samp_rate_in}, {}, [this]() { + UHD_LOG_INFO(get_unique_id(), "Calling resolver for `samp_rate_in'..."); + // nop + }); + } + + std::string get_unique_id() const + { + return "MOCK_STREAMER"; + } + + size_t get_num_input_ports() const + { + return _num_ports; + } + + size_t get_num_output_ports() const + { + return _num_ports; + } + + void issue_stream_cmd(uhd::stream_cmd_t stream_cmd, const size_t chan) + { + std::string cmd = + stream_cmd.stream_mode == uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS + ? "START" + : stream_cmd.stream_mode == uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS + ? "STOP" + : std::to_string(stream_cmd.num_samps); + auto scmd = action_info::make("stream_cmd"); + scmd->payload.insert(scmd->payload.begin(), cmd.begin(), cmd.end()); + + post_action({res_source_info::INPUT_EDGE, chan}, scmd); + } + +private: + property_t<double> _samp_rate_user{ + "samp_rate", 1e6, {res_source_info::USER}}; + property_t<double> _samp_rate_in{ + "samp_rate", 1e6, {res_source_info::INPUT_EDGE}}; + const size_t _num_ports; +}; + #endif /* INCLUDED_LIBUHD_TESTS_MOCK_NODES_HPP */ |