diff options
author | Lane Kolbly <lane.kolbly@ni.com> | 2020-06-22 17:22:56 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-06-25 13:36:08 -0500 |
commit | e9ae5fb949903cce1720b75b894401930cce1ebe (patch) | |
tree | 218233fd572d6318fcdfb6e661eae74fe88e485a | |
parent | e08830cbe5609645462190aa0c1a9e234353f38a (diff) | |
download | uhd-e9ae5fb949903cce1720b75b894401930cce1ebe.tar.gz uhd-e9ae5fb949903cce1720b75b894401930cce1ebe.tar.bz2 uhd-e9ae5fb949903cce1720b75b894401930cce1ebe.zip |
uhd: Add discoverable_features API
The "discoverable features" API handles how clients access the myriad
features we offer, without simply adding a million has_FOO and do_FOO
methods to radio_control and multi_usrp. discoverable_features allows
clients to query the existance of, enumerate, and ultimately they get
(by enum or by type) an object which implements their wanted feature.
-rw-r--r-- | host/include/uhd/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/include/uhd/features/CMakeLists.txt | 12 | ||||
-rw-r--r-- | host/include/uhd/features/discoverable_feature.hpp | 39 | ||||
-rw-r--r-- | host/include/uhd/features/discoverable_feature_getter_iface.hpp | 63 | ||||
-rw-r--r-- | host/tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/tests/discoverable_feature_test.cpp | 111 |
6 files changed, 227 insertions, 0 deletions
diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index f6bdd2a8f..cf3fa62de 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -7,6 +7,7 @@ # add_subdirectory(cal) +add_subdirectory(features) add_subdirectory(rfnoc) add_subdirectory(transport) add_subdirectory(types) diff --git a/host/include/uhd/features/CMakeLists.txt b/host/include/uhd/features/CMakeLists.txt new file mode 100644 index 000000000..efba46dc2 --- /dev/null +++ b/host/include/uhd/features/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +UHD_INSTALL(FILES + discoverable_feature.hpp + discoverable_feature_getter.hpp + DESTINATION ${INCLUDE_DIR}/uhd/features + COMPONENT headers +) diff --git a/host/include/uhd/features/discoverable_feature.hpp b/host/include/uhd/features/discoverable_feature.hpp new file mode 100644 index 000000000..dfb0ede5b --- /dev/null +++ b/host/include/uhd/features/discoverable_feature.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <stddef.h> +#include <memory> +#include <string> + +namespace uhd { namespace features { + +/*! + * The base class for discoverable features + * + * All discoverable features inherit from this class, which provides some basic + * functionality for features. + * + * Also note that all discoverable features must implement a static method + * get_feature_id() which returns a feature_id_t. + */ +class discoverable_feature +{ +public: + using sptr = std::shared_ptr<discoverable_feature>; + enum feature_id_t { + RESERVED0, + RESERVED1, + }; + + virtual ~discoverable_feature() = default; + + //! Returns a human-readonable string name of this feature. + virtual std::string get_feature_name() const = 0; +}; + +}} // namespace uhd::features diff --git a/host/include/uhd/features/discoverable_feature_getter_iface.hpp b/host/include/uhd/features/discoverable_feature_getter_iface.hpp new file mode 100644 index 000000000..9c759759f --- /dev/null +++ b/host/include/uhd/features/discoverable_feature_getter_iface.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhd/exception.hpp> +#include <uhd/features/discoverable_feature.hpp> +#include <vector> + +namespace uhd { namespace features { + +/*! Interface for discovering and accessing discoverable features. + */ +class discoverable_feature_getter_iface +{ +public: + virtual ~discoverable_feature_getter_iface() = default; + + //! Retrieves a feature of the specified type. + // + // Note that if the given feature type does not exist, this function will + // assert. The user should first check that the feature exists via the + // has_feature method. + // Usage: + // auto feature_ref = radio.get_feature<desired_feature_class>(); + template <typename T> + T& get_feature() + { + auto p = get_feature_ptr(T::get_feature_id()); + UHD_ASSERT_THROW(p); + auto typed_p = dynamic_cast<T*>(p.get()); + UHD_ASSERT_THROW(typed_p); + return *typed_p; + } + + //! Determines whether a given feature exists + // + // This function should be used to gate functionality before calling + // get_feature(). + template <typename T> + bool has_feature() + { + return bool(get_feature_ptr(T::get_feature_id())); + } + + //! Enumerate all discoverable features present on the device. + // + // Returns a vector (in no particular order) of the features that this + // device supports. + virtual std::vector<std::string> enumerate_features() = 0; + +private: + //! Get a shared pointer to a feature, if it exists. + // + // If the feature does not exist on the device, returns a null pointer. + virtual discoverable_feature::sptr get_feature_ptr( + discoverable_feature::feature_id_t feature_id) = 0; +}; + +}} // namespace uhd::features diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 8c8a1cbc3..47ca5b79f 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -32,6 +32,7 @@ set(test_sources constrained_device_args_test.cpp convert_test.cpp dict_test.cpp + discoverable_feature_test.cpp eeprom_utils_test.cpp error_test.cpp fp_compare_delta_test.cpp diff --git a/host/tests/discoverable_feature_test.cpp b/host/tests/discoverable_feature_test.cpp new file mode 100644 index 000000000..b75cb76f3 --- /dev/null +++ b/host/tests/discoverable_feature_test.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/features/discoverable_feature_getter_iface.hpp> +#include <boost/test/unit_test.hpp> +#include <iostream> + +using namespace uhd::features; + +class test_feature0 : public discoverable_feature +{ +public: + static discoverable_feature::feature_id_t get_feature_id() + { + return discoverable_feature::RESERVED0; + } + + std::string get_feature_name() const override + { + return "test_feature0"; + } +}; + +class test_feature1 : public discoverable_feature +{ +public: + static discoverable_feature::feature_id_t get_feature_id() + { + return discoverable_feature::RESERVED1; + } + + std::string get_feature_name() const override + { + return "test_feature1"; + } +}; + +class test_feature_getter : public discoverable_feature_getter_iface +{ +public: + test_feature_getter(bool feature0_enabled, bool feature1_enabled) + { + if (feature0_enabled) + _test_feature0.reset(new test_feature0()); + if (feature1_enabled) + _test_feature1.reset(new test_feature1); + } + + std::vector<std::string> enumerate_features() override + { + std::vector<std::string> features; + if (_test_feature0) + features.push_back(_test_feature0->get_feature_name()); + if (_test_feature1) + features.push_back(_test_feature1->get_feature_name()); + return features; + } + +private: + discoverable_feature::sptr get_feature_ptr( + discoverable_feature::feature_id_t feature_id) override + { + switch (feature_id) { + case discoverable_feature::RESERVED0: + return _test_feature0; + case discoverable_feature::RESERVED1: + return _test_feature1; + default: + return discoverable_feature::sptr(); + }; + } + + discoverable_feature::sptr _test_feature0; + discoverable_feature::sptr _test_feature1; +}; + +BOOST_AUTO_TEST_CASE(test_has_feature_works) +{ + test_feature_getter feature_getter(true, false); + BOOST_CHECK_EQUAL(feature_getter.has_feature<test_feature0>(), true); + BOOST_CHECK_EQUAL(feature_getter.has_feature<test_feature1>(), false); +} + +BOOST_AUTO_TEST_CASE(test_enumerate_feature_works) +{ + test_feature_getter feature_getter(true, true); + const auto features = feature_getter.enumerate_features(); + + // Note that ordering isn't strictly a requirement of the interface, + // we just leverage that here to demonstrate API usage concisely. + BOOST_CHECK_EQUAL(features[0], "test_feature0"); + BOOST_CHECK_EQUAL(features[1], "test_feature1"); +} + +BOOST_AUTO_TEST_CASE(test_get_feature_works) +{ + test_feature_getter feature_getter(true, true); + const auto f0 = feature_getter.get_feature<test_feature0>(); + const auto f1 = feature_getter.get_feature<test_feature1>(); + BOOST_CHECK_EQUAL(f0.get_feature_name(), "test_feature0"); + BOOST_CHECK_EQUAL(f1.get_feature_name(), "test_feature1"); +} + +BOOST_AUTO_TEST_CASE(test_get_feature_throws) +{ + test_feature_getter feature_getter(false, true); + BOOST_CHECK_THROW(feature_getter.get_feature<test_feature0>(), std::exception); +} |