diff options
-rw-r--r-- | host/include/uhd/rfnoc/node.hpp | 21 | ||||
-rw-r--r-- | host/include/uhd/rfnoc/property.hpp | 3 | ||||
-rw-r--r-- | host/lib/rfnoc/node.cpp | 21 | ||||
-rw-r--r-- | host/tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/tests/rfnoc_node_test.cpp | 31 | ||||
-rw-r--r-- | host/tests/rfnoc_property_test.cpp | 13 |
6 files changed, 85 insertions, 5 deletions
diff --git a/host/include/uhd/rfnoc/node.hpp b/host/include/uhd/rfnoc/node.hpp index b26546643..9d66c516a 100644 --- a/host/include/uhd/rfnoc/node.hpp +++ b/host/include/uhd/rfnoc/node.hpp @@ -135,6 +135,27 @@ public: * * Property resolution happens after all properties have been updated. * + * This function allows the client to override the \p instance parameter + * for each property key/value pair passed in via the \p props parameter. + * If the key consists of the property name, followed by a colon (':') and + * then a number, the number following the colon is used to determine + * which instance of the property this set pertains to, and the \p + * instance parameter is ignored for that property. (Note that if the key + * does not have the colon and instance number override syntax, then + * \p instance is still used to determine which instance of the property + * to set. For example, in the following call: + * + * node->set_properties("dog=10,cat:2=5,bird:0=0.5", 1) + * + * instance 1 of node's 'dog' property is set to 10, the 1 coming from the + * instance parameter, instance 2 of the node's 'cat' property is set to + * 5 due to the override syntax provided in the string, and instance 0 of + * the node's 'bird' property is set to 0.5 due to its override. + * + * If the instance override is malformed, that is, there is no + * number following the colon, or the number cannot be parsed as an + * integer, a value_error is thrown. + * * If a key in \p props is not a valid property of this block, a warning is * logged, but no error is raised. */ diff --git a/host/include/uhd/rfnoc/property.hpp b/host/include/uhd/rfnoc/property.hpp index a1e877440..a5c7246d1 100644 --- a/host/include/uhd/rfnoc/property.hpp +++ b/host/include/uhd/rfnoc/property.hpp @@ -36,6 +36,9 @@ public: property_base_t(const std::string& id, const res_source_info& source_info) : _id(id), _source_info(source_info) { + if(_id.find(':') != std::string::npos) { + throw uhd::value_error("Property ID `" + _id + "' contains invalid character!"); + } } //! Gets the ID (name) of this property diff --git a/host/lib/rfnoc/node.cpp b/host/lib/rfnoc/node.cpp index 0abbb0d3b..062645b93 100644 --- a/host/lib/rfnoc/node.cpp +++ b/host/lib/rfnoc/node.cpp @@ -48,11 +48,28 @@ std::vector<std::string> node_t::get_property_ids() const void node_t::set_properties(const uhd::device_addr_t& props, const size_t instance) { for (const auto& key : props.keys()) { + std::string local_key = key; + size_t local_instance = instance; + const size_t colon_pos = key.find(':'); + if (colon_pos != std::string::npos) { + // Extract the property ID and instance + local_key = key.substr(0, colon_pos); + std::string instance_part = key.substr(colon_pos + 1); + try { + local_instance = std::stoi(instance_part); + } catch (...) { + // If no number, or an invalid number is specified after the + // colon, throw a value_error. + throw uhd::value_error("Property id `" + local_key + + "' contains a malformed instance override!"); + } + } + property_base_t* prop_ref = - _find_property({res_source_info::USER, instance}, key); + _find_property({res_source_info::USER, local_instance}, local_key); if (!prop_ref) { RFNOC_LOG_WARNING("set_properties() cannot set property `" - << key << "': No such property."); + << local_key << "': No such property."); continue; } auto prop_access = _request_property_access(prop_ref, property_base_t::RW); diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index e06d4c6df..daf9f2976 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -46,6 +46,7 @@ set(test_sources narrow_cast_test.cpp property_test.cpp ranges_test.cpp + rfnoc_node_test.cpp scope_exit_test.cpp sensors_test.cpp soft_reg_test.cpp diff --git a/host/tests/rfnoc_node_test.cpp b/host/tests/rfnoc_node_test.cpp index b90835081..283d0bf38 100644 --- a/host/tests/rfnoc_node_test.cpp +++ b/host/tests/rfnoc_node_test.cpp @@ -21,6 +21,8 @@ public: std::cout << "Calling clean callback for user prop" << std::endl; this->user_prop_cb_called = true; }); + register_property(&multi_instance_prop_0); + register_property(&multi_instance_prop_1); register_property(&_double_prop_in); register_property(&_double_prop_out); @@ -80,6 +82,10 @@ private: "double_prop", 0.0, {res_source_info::INPUT_EDGE, 0}}; property_t<double> _double_prop_out{ "double_prop", 0.0, {res_source_info::OUTPUT_EDGE, 1}}; + property_t<double> multi_instance_prop_0{ + "multi_instance_prop", 0.0, {res_source_info::USER, 0}}; + property_t<double> multi_instance_prop_1{ + "multi_instance_prop", 0.0, {res_source_info::USER, 1}}; const size_t _num_input_ports; const size_t _num_output_ports; @@ -102,7 +108,7 @@ BOOST_AUTO_TEST_CASE(test_node_prop_access) BOOST_CHECK(TN1.get_unique_id() != TN2.get_unique_id()); auto user_prop_ids = TN1.get_property_ids(); - BOOST_REQUIRE_EQUAL(user_prop_ids.size(), 1); + BOOST_REQUIRE_EQUAL(user_prop_ids.size(), 3); BOOST_CHECK_EQUAL(user_prop_ids[0], "double_prop"); BOOST_REQUIRE_THROW(TN1.get_property<int>("nonexistant_prop"), uhd::lookup_error); @@ -113,6 +119,20 @@ BOOST_AUTO_TEST_CASE(test_node_prop_access) BOOST_CHECK_EQUAL(TN1.get_property<double>("double_prop"), 0.0); + // Check that set_properties() works with the override specification + TN1.set_properties( + uhd::device_addr_t("multi_instance_prop:0=1.234,multi_instance_prop:1=-5.678"), + 5); + BOOST_CHECK_EQUAL(TN1.get_property<double>("multi_instance_prop", 0), 1.234); + BOOST_CHECK_EQUAL(TN1.get_property<double>("multi_instance_prop", 1), -5.678); + + // And check that it throws an exception with a bad override specification + BOOST_REQUIRE_THROW( + TN1.set_properties(uhd::device_addr_t("multi_instance_prop:")), uhd::value_error); + BOOST_REQUIRE_THROW( + TN1.set_properties(uhd::device_addr_t("multi_instance_prop:chicken")), + uhd::value_error); + BOOST_REQUIRE_THROW(TN1.set_property<int>("nonexistant_prop", 5), uhd::lookup_error); // If this next test fails, RTTI is not available. There might be cases when // that's expected, and when we encounter those we'll reconsider the test. @@ -131,8 +151,13 @@ BOOST_AUTO_TEST_CASE(test_node_accessor) return (prop->get_src_info().type == res_source_info::USER); }); - BOOST_CHECK_EQUAL(user_props.size(), 1); - BOOST_CHECK_EQUAL((*user_props.begin())->get_id(), "double_prop"); + BOOST_CHECK_EQUAL(user_props.size(), 3); + std::map<std::string, int> prop_count; + for (const auto& prop : user_props) { + prop_count[prop->get_id()]++; + } + BOOST_CHECK_EQUAL(prop_count["double_prop"], 1); + BOOST_CHECK_EQUAL(prop_count["multi_instance_prop"], 2); BOOST_CHECK((*user_props.begin())->get_src_info().type == res_source_info::USER); BOOST_CHECK(!TN1.user_prop_cb_called); diff --git a/host/tests/rfnoc_property_test.cpp b/host/tests/rfnoc_property_test.cpp index eb22424b1..d1a8ba981 100644 --- a/host/tests/rfnoc_property_test.cpp +++ b/host/tests/rfnoc_property_test.cpp @@ -65,6 +65,19 @@ BOOST_AUTO_TEST_CASE(test_get_set) BOOST_CHECK(prop_i.is_dirty()); } +BOOST_AUTO_TEST_CASE(test_valid_names) +{ + bool value_error_caught = false; + try { + property_t<int> prop_i{"int_prop:0", 10, {res_source_info::USER, 0}}; + } catch(const uhd::value_error& e) { + value_error_caught = true; + } catch(...) { + } + + BOOST_CHECK(value_error_caught); +} + BOOST_AUTO_TEST_CASE(test_lock) { prop_accessor_t prop_accessor; |