From e9ae5fb949903cce1720b75b894401930cce1ebe Mon Sep 17 00:00:00 2001 From: Lane Kolbly Date: Mon, 22 Jun 2020 17:22:56 -0500 Subject: 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. --- host/include/uhd/CMakeLists.txt | 1 + host/include/uhd/features/CMakeLists.txt | 12 +++ host/include/uhd/features/discoverable_feature.hpp | 39 ++++++++ .../features/discoverable_feature_getter_iface.hpp | 63 ++++++++++++ host/tests/CMakeLists.txt | 1 + host/tests/discoverable_feature_test.cpp | 111 +++++++++++++++++++++ 6 files changed, 227 insertions(+) create mode 100644 host/include/uhd/features/CMakeLists.txt create mode 100644 host/include/uhd/features/discoverable_feature.hpp create mode 100644 host/include/uhd/features/discoverable_feature_getter_iface.hpp create mode 100644 host/tests/discoverable_feature_test.cpp 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 +#include +#include + +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; + 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 +#include +#include + +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(); + template + T& get_feature() + { + auto p = get_feature_ptr(T::get_feature_id()); + UHD_ASSERT_THROW(p); + auto typed_p = dynamic_cast(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 + 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 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 +#include +#include + +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 enumerate_features() override + { + std::vector 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(), true); + BOOST_CHECK_EQUAL(feature_getter.has_feature(), 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(); + const auto f1 = feature_getter.get_feature(); + 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(), std::exception); +} -- cgit v1.2.3