aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLane Kolbly <lane.kolbly@ni.com>2020-06-22 17:22:56 -0500
committerAaron Rossetto <aaron.rossetto@ni.com>2020-06-25 13:36:08 -0500
commite9ae5fb949903cce1720b75b894401930cce1ebe (patch)
tree218233fd572d6318fcdfb6e661eae74fe88e485a
parente08830cbe5609645462190aa0c1a9e234353f38a (diff)
downloaduhd-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.txt1
-rw-r--r--host/include/uhd/features/CMakeLists.txt12
-rw-r--r--host/include/uhd/features/discoverable_feature.hpp39
-rw-r--r--host/include/uhd/features/discoverable_feature_getter_iface.hpp63
-rw-r--r--host/tests/CMakeLists.txt1
-rw-r--r--host/tests/discoverable_feature_test.cpp111
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);
+}