diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bebd7564..c025261b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,7 +324,11 @@ set(VIAMCPPSDK_XTL_VERSION_MINIMUM 0.7.2) set(VIAMCPPSDK_XTENSOR_VERSION_MINIMUM 0.24.3) # Time to find `BOOST`. -find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED COMPONENTS headers log program_options) +if (VIAMCPPSDK_BUILD_TESTS) + set(VIAMCPPSDK_BOOST_TEST "unit_test_framework") +endif() + +find_package(Boost ${VIAMCPPSDK_BOOST_VERSION_MINIMUM} REQUIRED COMPONENTS headers log program_options ${VIAMCPPSDK_BOOST_TEST}) # Time to find `protobuf` and `gRPC[++]`. Normally this would just be # something like `find_package(gRPC CONFIG REQUIRED)`, and diff --git a/src/viam/examples/camera/example_camera.cpp b/src/viam/examples/camera/example_camera.cpp index bc1b0ab9a..d1eb9b80c 100644 --- a/src/viam/examples/camera/example_camera.cpp +++ b/src/viam/examples/camera/example_camera.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,11 @@ int main() { using std::endl; namespace vs = ::viam::sdk; try { + // Every Viam C++ SDK program must have one and only one Instance object which is created + // before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + vs::Instance inst; + // If you want to connect to a remote robot, this should be the url of the robot // Ex: xxx.xxx.viam.cloud std::string robot_address("localhost:8080"); diff --git a/src/viam/examples/dial/example_dial.cpp b/src/viam/examples/dial/example_dial.cpp index efa783dfb..f39522c26 100644 --- a/src/viam/examples/dial/example_dial.cpp +++ b/src/viam/examples/dial/example_dial.cpp @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -16,6 +17,10 @@ using namespace viam::sdk; int main() { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + Instance inst; + const char* uri = ""; DialOptions dial_options; std::string type = ""; diff --git a/src/viam/examples/dial_api_key/example_dial_api_key.cpp b/src/viam/examples/dial_api_key/example_dial_api_key.cpp index 4c47beffe..b723e4227 100644 --- a/src/viam/examples/dial_api_key/example_dial_api_key.cpp +++ b/src/viam/examples/dial_api_key/example_dial_api_key.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -18,6 +19,10 @@ using namespace viam::sdk; namespace po = boost::program_options; int main(int argc, char* argv[]) { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + Instance inst; + po::options_description desc("Allowed options"); desc.add_options()("help", "List options and exit")( "uri", po::value(), "URI of robot")( diff --git a/src/viam/examples/mlmodel/example_audio_classification_client.cpp b/src/viam/examples/mlmodel/example_audio_classification_client.cpp index 95e26f57c..6be570449 100644 --- a/src/viam/examples/mlmodel/example_audio_classification_client.cpp +++ b/src/viam/examples/mlmodel/example_audio_classification_client.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -78,6 +79,10 @@ constexpr char kRobotConfigTemplate[] = R"( } // namespace int main(int argc, char* argv[]) try { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + viam::sdk::Instance inst; + // Build up our command line options. The example operates in two // modes. In the "--generate" mode, it takes command line // parameters needed to satisfy the interpolation points in the diff --git a/src/viam/examples/modules/complex/client.cpp b/src/viam/examples/modules/complex/client.cpp index ed321abd5..4c26f055f 100644 --- a/src/viam/examples/modules/complex/client.cpp +++ b/src/viam/examples/modules/complex/client.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,10 @@ using namespace viam::sdk; int main() { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + Instance inst; + const char* uri = "http://localhost:8080/"; // replace with your URI if connecting securely DialOptions dial_options; dial_options.set_allow_insecure_downgrade(true); // set to false if connecting securely @@ -37,8 +42,8 @@ int main() { // Register custom gizmo and summation clients so robot client can access resources // of that type from the server. - Registry::register_resource_client(); - Registry::register_resource_client(); + Registry::get().register_resource_client(); + Registry::get().register_resource_client(); // Connect to robot. std::shared_ptr robot = RobotClient::at_address(address, options); diff --git a/src/viam/examples/modules/complex/main.cpp b/src/viam/examples/modules/complex/main.cpp index 59f590733..253e69d78 100644 --- a/src/viam/examples/modules/complex/main.cpp +++ b/src/viam/examples/modules/complex/main.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -24,11 +25,15 @@ using namespace viam::sdk; int main(int argc, char** argv) { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + Instance inst; + Model mybase_model("viam", "base", "mybase"); // Make sure to explicity register resources with custom APIs. - Registry::register_resource_server(); - Registry::register_resource_server(); + Registry::get().register_resource_server(); + Registry::get().register_resource_server(); std::shared_ptr mybase_mr = std::make_shared( API::get(), diff --git a/src/viam/examples/modules/complex/test_complex_module.cpp b/src/viam/examples/modules/complex/test_complex_module.cpp index 4984df8f0..4a178271f 100644 --- a/src/viam/examples/modules/complex/test_complex_module.cpp +++ b/src/viam/examples/modules/complex/test_complex_module.cpp @@ -24,8 +24,9 @@ using namespace viam::sdktests; struct RegisterGizmoAndSummationFixture { RegisterGizmoAndSummationFixture() { - Registry::register_resource(); - Registry::register_resource(); + auto& registry = Registry::get(); + registry.register_resource(); + registry.register_resource(); } // Test teardown is a noop; diff --git a/src/viam/examples/modules/simple/client.cpp b/src/viam/examples/modules/simple/client.cpp index fcaab5eff..f08410336 100644 --- a/src/viam/examples/modules/simple/client.cpp +++ b/src/viam/examples/modules/simple/client.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -10,6 +11,10 @@ using namespace viam::sdk; int main() { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + Instance inst; + const char* uri = "http://localhost:8080/"; // replace with your URI if connecting securely DialOptions dial_options; dial_options.set_allow_insecure_downgrade(true); // set to false if connecting securely diff --git a/src/viam/examples/modules/simple/main.cpp b/src/viam/examples/modules/simple/main.cpp index cfd783f66..e85c2fe3a 100644 --- a/src/viam/examples/modules/simple/main.cpp +++ b/src/viam/examples/modules/simple/main.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -75,6 +76,10 @@ ProtoStruct MySensor::get_readings(const ProtoStruct&) { } int main(int argc, char** argv) try { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + Instance inst; + Model mysensor_model("viam", "sensor", "mysensor"); std::shared_ptr mr = std::make_shared( diff --git a/src/viam/examples/modules/tflite/main.cpp b/src/viam/examples/modules/tflite/main.cpp index 44cd8e284..65ca8c191 100644 --- a/src/viam/examples/modules/tflite/main.cpp +++ b/src/viam/examples/modules/tflite/main.cpp @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -723,6 +724,10 @@ class MLModelServiceTFLite : public vsdk::MLModelService, }; int serve(const std::string& socket_path) try { + // Every Viam C++ SDK program must have one and only one Instance object which is created before + // any other C++ SDK objects and stays alive until all Viam C++ SDK objects are destroyed. + vsdk::Instance inst; + // Create a new model registration for the service. auto module_registration = std::make_shared( // Identify that this resource offers the MLModelService API @@ -737,7 +742,7 @@ int serve(const std::string& socket_path) try { }); // Register the newly created registration with the Registry. - vsdk::Registry::register_model(module_registration); + Registry::get().register_model(module_registration); // Construct the module service and tell it where to place the socket path. auto module_service = std::make_shared(socket_path); diff --git a/src/viam/examples/motor/example_motor.cpp b/src/viam/examples/motor/example_motor.cpp index c4d8023d5..3d5a640ef 100644 --- a/src/viam/examples/motor/example_motor.cpp +++ b/src/viam/examples/motor/example_motor.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,11 @@ int main() { namespace vs = ::viam::sdk; try { + // Every Viam C++ SDK program must have one and only one Instance object which is created + // before any other C++ SDK objects and stays alive until all Viam C++ SDK objects are + // destroyed. + vs::Instance inst; + // If you want to connect to a remote robot, this should be the url of the robot // Ex: xxx.xxx.viam.cloud std::string robot_address("localhost:8080"); diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt index 6b73c7b98..b69b586f9 100644 --- a/src/viam/sdk/CMakeLists.txt +++ b/src/viam/sdk/CMakeLists.txt @@ -50,6 +50,7 @@ target_sources(viamsdk PRIVATE common/client_helper.cpp common/exception.cpp + common/instance.cpp common/linear_algebra.cpp common/pose.cpp common/proto_value.cpp @@ -145,6 +146,7 @@ target_sources(viamsdk FILES ../../viam/sdk/common/client_helper.hpp ../../viam/sdk/common/exception.hpp + ../../viam/sdk/common/instance.hpp ../../viam/sdk/common/linear_algebra.hpp ../../viam/sdk/common/pose.hpp ../../viam/sdk/common/proto_convert.hpp diff --git a/src/viam/sdk/common/instance.cpp b/src/viam/sdk/common/instance.cpp new file mode 100644 index 000000000..e3a84ea65 --- /dev/null +++ b/src/viam/sdk/common/instance.cpp @@ -0,0 +1,57 @@ +#include + +#include + +#include +#include +#include + +namespace viam { +namespace sdk { + +namespace { + +// Memory region sentinel to check if object is being destroyed. +std::aligned_storage_t sentinel; + +std::atomic current_instance{nullptr}; + +} // namespace + +Instance::Instance() { + Instance* expected = nullptr; + + if (!current_instance.compare_exchange_strong(expected, this)) { + throw Exception("tried to create duplicate instance"); + } + + impl_ = std::make_unique(); + impl_->registry.initialize(); +} + +Instance::~Instance() { + current_instance.store(reinterpret_cast(&sentinel)); + impl_.reset(); +} + +Instance& Instance::current(Instance::Creation creation) { + if (!current_instance.load()) { + if (creation == Creation::if_needed) { + // This variable declaration calls the default ctor, storing a current instance. + static Instance inst; // NOLINT (misc-const-correctness) + } else { + throw Exception("Instance has not yet been created"); + } + } + + Instance* current = current_instance.load(); + + if (current == reinterpret_cast(&sentinel)) { + throw Exception("instance was destroyed"); + } + + return *current; +} + +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/common/instance.hpp b/src/viam/sdk/common/instance.hpp new file mode 100644 index 000000000..096815943 --- /dev/null +++ b/src/viam/sdk/common/instance.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace viam { +namespace sdk { + +/// @brief Instance management for Viam C++ SDK applications. +/// This is a single instance class which is responsible for global setup and teardown related to +/// the SDK. An Instance must be constructed before doing anything else in a program, and it must +/// remain alive in a valid state for the duration of the program. Creating multiple Instance +/// objects in the same program is an error. +class Instance { + public: + /// @brief Enumeration for creation behavior of @ref current + enum class Creation { + open_existing, ///< Instance must already exist + if_needed ///< Use existing instance if present, else create one. + }; + + Instance(); + ~Instance(); + + /// @brief Get the current Instance according to the Creation behavior. + /// @throws an @ref Exception if current(Creation::open_existing) is called when an instance has + /// not yet been constructed. + static Instance& current(Creation); + + private: + friend class Registry; + + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/common/private/instance.hpp b/src/viam/sdk/common/private/instance.hpp new file mode 100644 index 000000000..b1728de8c --- /dev/null +++ b/src/viam/sdk/common/private/instance.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace viam { +namespace sdk { + +struct Instance::Impl { + Registry registry; +}; + +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/module/service.cpp b/src/viam/sdk/module/service.cpp index 53f1fe9bc..911be9bb3 100644 --- a/src/viam/sdk/module/service.cpp +++ b/src/viam/sdk/module/service.cpp @@ -57,7 +57,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service { std::shared_ptr res; const Dependencies deps = parent.get_dependencies_(&request->dependencies(), cfg.name()); const std::shared_ptr reg = - Registry::lookup_model(cfg.api(), cfg.model()); + Registry::get().lookup_model(cfg.api(), cfg.model()); if (reg) { try { res = reg->construct_resource(deps, cfg); @@ -111,7 +111,8 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service { BOOST_LOG_TRIVIAL(error) << "unable to stop resource: " << err.what(); } - const std::shared_ptr reg = Registry::lookup_model(cfg.name()); + const std::shared_ptr reg = + Registry::get().lookup_model(cfg.name()); if (reg) { try { const std::shared_ptr res = reg->construct_resource(deps, cfg); @@ -131,7 +132,7 @@ struct ModuleService::ServiceImpl : viam::module::v1::ModuleService::Service { ResourceConfig cfg = from_proto(proto); const std::shared_ptr reg = - Registry::lookup_model(cfg.api(), cfg.model()); + Registry::get().lookup_model(cfg.api(), cfg.model()); if (!reg) { return grpc::Status(grpc::UNKNOWN, "unable to validate resource " + cfg.resource_name().name() + @@ -231,7 +232,7 @@ ModuleService::ModuleService(int argc, set_logger_severity_from_args(argc, argv); for (auto&& mr : registrations) { - Registry::register_model(mr); + Registry::get().register_model(mr); add_model_from_registry(mr->api(), mr->model()); } } @@ -274,7 +275,7 @@ void ModuleService::add_model_from_registry_inlock_(API api, Model model, const std::lock_guard&) { const std::shared_ptr creator = - Registry::lookup_resource_server(api); + Registry::get().lookup_resource_server(api); std::string name; if (creator && creator->service_descriptor()) { name = creator->service_descriptor()->full_name(); diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp index 2b3dad0c3..de74d314d 100644 --- a/src/viam/sdk/registry/registry.cpp +++ b/src/viam/sdk/registry/registry.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -90,6 +91,12 @@ const Model& ModelRegistration::model() const { return model_; }; +Registry& Registry::get() { + static Registry& result = Instance::current(Instance::Creation::open_existing).impl_->registry; + + return result; +} + void Registry::register_model(std::shared_ptr resource) { std::string reg_key = resource->api().to_string() + "/" + resource->model().to_string(); if (resources_.find(reg_key) != resources_.end()) { @@ -124,7 +131,7 @@ void Registry::register_resource_client_( } std::shared_ptr Registry::lookup_model_inlock_( - const std::string& name, const std::lock_guard&) { + const std::string& name, const std::lock_guard&) const { if (resources_.find(name) == resources_.end()) { return nullptr; } @@ -132,19 +139,20 @@ std::shared_ptr Registry::lookup_model_inlock_( return resources_.at(name); } -std::shared_ptr Registry::lookup_model(const std::string& name) { +std::shared_ptr Registry::lookup_model(const std::string& name) const { const std::lock_guard lock(lock_); return lookup_model_inlock_(name, lock); } std::shared_ptr Registry::lookup_model(const API& api, - const Model& model) { + const Model& model) const { const std::lock_guard lock(lock_); const std::string name = api.to_string() + "/" + model.to_string(); return lookup_model_inlock_(name, lock); } -std::shared_ptr Registry::lookup_resource_server(const API& api) { +std::shared_ptr Registry::lookup_resource_server( + const API& api) const { const std::lock_guard lock(lock_); if (server_apis_.find(api) == server_apis_.end()) { return nullptr; @@ -153,7 +161,8 @@ std::shared_ptr Registry::lookup_resource_serv return server_apis_.at(api); } -std::shared_ptr Registry::lookup_resource_client(const API& api) { +std::shared_ptr Registry::lookup_resource_client( + const API& api) const { const std::lock_guard lock(lock_); if (client_apis_.find(api) == client_apis_.end()) { return nullptr; @@ -173,12 +182,12 @@ const google::protobuf::ServiceDescriptor* Registry::get_service_descriptor_( } const std::unordered_map>& -Registry::registered_resource_servers() { +Registry::registered_resource_servers() const { return server_apis_; } const std::unordered_map>& -Registry::registered_models() { +Registry::registered_models() const { return resources_; } @@ -186,48 +195,35 @@ const google::protobuf::ServiceDescriptor* ResourceServerRegistration::service_d return service_descriptor_; } -void register_resources() { +void Registry::register_resources() { // Register all components - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); // Register all services - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); - Registry::register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); + register_resource(); } void Registry::initialize() { - const std::lock_guard lock(lock_); - if (initialized_) { - BOOST_LOG_TRIVIAL(warning) - << "Attempted to initialize the Registry but it was already initialized."; - return; - } - initialized_ = true; register_resources(); grpc::reflection::InitProtoReflectionServerBuilderPlugin(); } -std::unordered_map> Registry::resources_; -std::unordered_map> Registry::client_apis_; -std::unordered_map> Registry::server_apis_; -std::mutex Registry::lock_; -bool Registry::initialized_{false}; - } // namespace sdk } // namespace viam diff --git a/src/viam/sdk/registry/registry.hpp b/src/viam/sdk/registry/registry.hpp index b00d98833..c1a5055a3 100644 --- a/src/viam/sdk/registry/registry.hpp +++ b/src/viam/sdk/registry/registry.hpp @@ -103,26 +103,28 @@ class ModelRegistration { /// @brief A registry of known resources. class Registry { public: + /// @brief Get the application-wide instance of Registry. + static Registry& get(); + /// @brief Registers a resource with the Registry. /// @param resource An object containing resource registration information. /// @throws `Exception` if the resource has already been registered. - static void register_model(std::shared_ptr resource); + void register_model(std::shared_ptr resource); /// @brief Lookup a given registered resource. /// @param name The name of the resource to lookup. /// @return a `shared_ptr` to the resource's registration data. - static std::shared_ptr lookup_model(const std::string& name); + std::shared_ptr lookup_model(const std::string& name) const; /// @brief Lookup a given registered resource. /// @param api The api of the resource to lookup. /// @param model The model of the resource to lookup. /// @return a `shared_ptr` to the resource's registration data. - static std::shared_ptr lookup_model(const API& api, - const Model& model); + std::shared_ptr lookup_model(const API& api, const Model& model) const; /// @brief Register a resource client constructor template - static void register_resource_client() { + void register_resource_client() { class ResourceClientRegistration2 final : public ResourceClientRegistration { public: using ResourceClientRegistration::ResourceClientRegistration; @@ -139,7 +141,7 @@ class Registry { /// @brief Register a resource server constructor. template - static void register_resource_server() { + void register_resource_server() { class ResourceServerRegistration2 final : public ResourceServerRegistration { public: using ResourceServerRegistration::ResourceServerRegistration; @@ -159,7 +161,7 @@ class Registry { /// @brief Register resource client and server constructors template - static void register_resource() { + void register_resource() { register_resource_client(); register_resource_server(); } @@ -167,44 +169,50 @@ class Registry { /// @brief Lookup a registered server api. /// @param api The api to lookup. /// @return A `shared_ptr` to the registered api's `ResourceServerRegistration`. - static std::shared_ptr lookup_resource_server(const API& api); + std::shared_ptr lookup_resource_server(const API& api) const; /// @brief Lookup a registered client api. /// @param api The api to lookup. /// @return A `shared_ptr` to the registered api's `ResourceClientRegistration`. - static std::shared_ptr lookup_resource_client(const API& api); + std::shared_ptr lookup_resource_client(const API& api) const; /// @brief Provide information on registered resource models. /// @return A map from name to `ModelRegistration` of all registered resource models. - static const std::unordered_map>& - registered_models(); + const std::unordered_map>& + registered_models() const; /// @brief Provide access to registered resources. /// @return A map from `API` to `ResourceServerRegistration` of all registered resources. - static const std::unordered_map>& - registered_resource_servers(); + const std::unordered_map>& + registered_resource_servers() const; + + private: + friend class Instance; + Registry() = default; /// @brief Initialized the Viam registry. No-op if it has already been called. - static void initialize(); + void initialize(); - private: - static std::mutex lock_; - static bool initialized_; - static std::unordered_map> resources_; - static std::unordered_map> client_apis_; - static std::unordered_map> server_apis_; + mutable std::mutex lock_; + + std::unordered_map> resources_; + + std::unordered_map> client_apis_; + std::unordered_map> server_apis_; + + void register_resources(); - static void register_resource_server_( + void register_resource_server_( API api, std::shared_ptr resource_registration); - static void register_resource_client_( + void register_resource_client_( API api, std::shared_ptr resource_registration); static const google::protobuf::ServiceDescriptor* get_service_descriptor_( const char* service_full_name); - static std::shared_ptr lookup_model_inlock_( - const std::string& name, const std::lock_guard&); + std::shared_ptr lookup_model_inlock_( + const std::string& name, const std::lock_guard&) const; }; } // namespace sdk diff --git a/src/viam/sdk/robot/client.cpp b/src/viam/sdk/robot/client.cpp index a7f1f3b0f..ab3789567 100644 --- a/src/viam/sdk/robot/client.cpp +++ b/src/viam/sdk/robot/client.cpp @@ -178,7 +178,8 @@ void RobotClient::refresh() { // are being properly registered from name.subtype(), or update what we're // using for lookup const std::shared_ptr rs = - Registry::lookup_resource_client({name.namespace_(), name.type(), name.subtype()}); + Registry::get().lookup_resource_client( + {name.namespace_(), name.type(), name.subtype()}); if (rs) { try { const std::shared_ptr rpc_client = @@ -225,9 +226,7 @@ RobotClient::RobotClient(std::shared_ptr channel) : channel_(channel->channel()), viam_channel_(std::move(channel)), should_close_channel_(false), - impl_(std::make_unique(RobotService::NewStub(channel_))) { - Registry::initialize(); -} + impl_(std::make_unique(RobotService::NewStub(channel_))) {} std::vector RobotClient::resource_names() const { const std::lock_guard lock(lock_); diff --git a/src/viam/sdk/robot/client.hpp b/src/viam/sdk/robot/client.hpp index 9d63bd14b..50fda612c 100644 --- a/src/viam/sdk/robot/client.hpp +++ b/src/viam/sdk/robot/client.hpp @@ -147,18 +147,24 @@ class RobotClient { status get_machine_status() const; private: + void refresh_every(); + std::vector> threads_; + std::atomic should_refresh_; unsigned int refresh_interval_; + std::shared_ptr channel_; std::shared_ptr viam_channel_; bool should_close_channel_; + struct impl; std::unique_ptr impl_; + mutable std::mutex lock_; + std::vector resource_names_; ResourceManager resource_manager_; - void refresh_every(); }; } // namespace sdk diff --git a/src/viam/sdk/rpc/server.cpp b/src/viam/sdk/rpc/server.cpp index 408f23bf6..9964abbac 100644 --- a/src/viam/sdk/rpc/server.cpp +++ b/src/viam/sdk/rpc/server.cpp @@ -19,8 +19,7 @@ Server::Server() : builder_(std::make_unique()) { builder_->SetMaxReceiveMessageSize(kMaxMessageSize); builder_->SetMaxSendMessageSize(kMaxMessageSize); builder_->SetMaxMessageSize(kMaxMessageSize); - Registry::initialize(); - for (const auto& rr : Registry::registered_resource_servers()) { + for (const auto& rr : Registry::get().registered_resource_servers()) { auto new_manager = std::make_shared(); auto server = rr.second->create_resource_server(new_manager, *this); managed_servers_.emplace(rr.first, std::move(server)); diff --git a/src/viam/sdk/tests/CMakeLists.txt b/src/viam/sdk/tests/CMakeLists.txt index de1fbf0b7..fa2d1560d 100644 --- a/src/viam/sdk/tests/CMakeLists.txt +++ b/src/viam/sdk/tests/CMakeLists.txt @@ -47,6 +47,7 @@ target_include_directories(viamsdk_test target_link_libraries(viamsdk_test PUBLIC viam-cpp-sdk::viamsdk + PUBLIC Boost::unit_test_framework ) viamcppsdk_link_viam_api(viamsdk_test PUBLIC) diff --git a/src/viam/sdk/tests/mocks/mock_robot.cpp b/src/viam/sdk/tests/mocks/mock_robot.cpp index cdef0fe78..ce9a62884 100644 --- a/src/viam/sdk/tests/mocks/mock_robot.cpp +++ b/src/viam/sdk/tests/mocks/mock_robot.cpp @@ -26,7 +26,7 @@ std::vector registered_models_for_resource(const std::shared_ptr std::string resource_type; std::string resource_subtype; std::vector resource_names; - for (const auto& kv : Registry::registered_models()) { + for (const auto& kv : Registry::get().registered_models()) { const std::shared_ptr reg = kv.second; if (reg->api() == resource->api()) { resource_type = reg->api().resource_type(); diff --git a/src/viam/sdk/tests/test_mlmodel.cpp b/src/viam/sdk/tests/test_mlmodel.cpp index 5d2db8010..8bc6a62f0 100644 --- a/src/viam/sdk/tests/test_mlmodel.cpp +++ b/src/viam/sdk/tests/test_mlmodel.cpp @@ -12,20 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +#define BOOST_TEST_MODULE test module test_mlmodel #include #include #include #include +#include #include #include #include -#define BOOST_TEST_MODULE test module test_mlmodel -#include - namespace viam { namespace sdk { diff --git a/src/viam/sdk/tests/test_robot.cpp b/src/viam/sdk/tests/test_robot.cpp index 9f1093d49..2018ed36b 100644 --- a/src/viam/sdk/tests/test_robot.cpp +++ b/src/viam/sdk/tests/test_robot.cpp @@ -62,6 +62,8 @@ void robot_client_to_mocks_pipeline(F&& test_case) { } BOOST_AUTO_TEST_CASE(test_registering_resources) { + auto& registry = Registry::get(); + // To test with mock resources we need to be able to create them, which means registering // constructors. This tests that we register correctly. Model camera_model("fake", "fake", "mock_camera"); @@ -70,7 +72,7 @@ BOOST_AUTO_TEST_CASE(test_registering_resources) { camera_model, [](Dependencies, ResourceConfig cfg) { return camera::MockCamera::get_mock_camera(); }, [](ResourceConfig cfg) -> std::vector { return {}; }); - Registry::register_model(cr); + registry.register_model(cr); Model generic_model("fake", "fake", "mock_generic"); std::shared_ptr gr = std::make_shared( @@ -80,7 +82,7 @@ BOOST_AUTO_TEST_CASE(test_registering_resources) { return generic::MockGenericComponent::get_mock_generic(); }, [](ResourceConfig cfg) -> std::vector { return {}; }); - Registry::register_model(gr); + registry.register_model(gr); Model motor_model("fake", "fake", "mock_motor"); std::shared_ptr mr = std::make_shared( @@ -88,11 +90,11 @@ BOOST_AUTO_TEST_CASE(test_registering_resources) { motor_model, [](Dependencies, ResourceConfig cfg) { return motor::MockMotor::get_mock_motor(); }, [](ResourceConfig cfg) -> std::vector { return {}; }); - Registry::register_model(mr); + registry.register_model(mr); - BOOST_CHECK(Registry::lookup_model(API::get(), camera_model)); - BOOST_CHECK(Registry::lookup_model(API::get(), generic_model)); - BOOST_CHECK(Registry::lookup_model(API::get(), motor_model)); + BOOST_CHECK(registry.lookup_model(API::get(), camera_model)); + BOOST_CHECK(registry.lookup_model(API::get(), generic_model)); + BOOST_CHECK(registry.lookup_model(API::get(), motor_model)); } BOOST_AUTO_TEST_CASE(test_resource_names) { diff --git a/src/viam/sdk/tests/test_utils.cpp b/src/viam/sdk/tests/test_utils.cpp index 0ce5c83ca..9e76e2568 100644 --- a/src/viam/sdk/tests/test_utils.cpp +++ b/src/viam/sdk/tests/test_utils.cpp @@ -11,7 +11,6 @@ namespace viam { namespace sdktests { - using namespace viam::sdk; ProtoStruct fake_map() { diff --git a/src/viam/sdk/tests/test_utils.hpp b/src/viam/sdk/tests/test_utils.hpp index 65ca53fc1..bb69e2499 100644 --- a/src/viam/sdk/tests/test_utils.hpp +++ b/src/viam/sdk/tests/test_utils.hpp @@ -2,6 +2,9 @@ #include +#include + +#include #include #include #include @@ -10,6 +13,14 @@ namespace viam { namespace sdktests { +struct GlobalFixture { + GlobalFixture() { + (void)sdk::Instance::current(sdk::Instance::Creation::if_needed); + } +}; + +BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture); + using namespace viam::sdk; ProtoStruct fake_map(); @@ -62,7 +73,8 @@ void client_to_mock_pipeline(std::shared_ptr mock, F&& test_case) { auto test_server = TestServer(server); auto grpc_channel = test_server.grpc_in_process_channel(); - auto resource_client = Registry::lookup_resource_client(API::get()) + auto resource_client = sdk::Registry::get() + .lookup_resource_client(API::get()) ->create_rpc_client(mock->name(), std::move(grpc_channel)); // Run the passed-in test case on the created stack and give access to the