diff --git a/src/viam/api/CMakeLists.txt b/src/viam/api/CMakeLists.txt index 3e287547e..413da8d5e 100644 --- a/src/viam/api/CMakeLists.txt +++ b/src/viam/api/CMakeLists.txt @@ -113,6 +113,16 @@ if (VIAMCPPSDK_USE_DYNAMIC_PROTOS) ) endif() + # List of names of non-compilable generated code + # The Switch component generates protobuf which uses `switch` as a namespace. + # This needs to be processed below. + set(VIAMCPPSDK_SWITCH_REPLACE_PATHS + ${PROTO_GEN_DIR}/component/switch/v1/switch.grpc.pb.cc + ${PROTO_GEN_DIR}/component/switch/v1/switch.grpc.pb.h + ${PROTO_GEN_DIR}/component/switch/v1/switch.pb.cc + ${PROTO_GEN_DIR}/component/switch/v1/switch.pb.h + ) + add_custom_command( OUTPUT # Unfortunately, there isn't a good way to know in advance what @@ -201,6 +211,10 @@ if (VIAMCPPSDK_USE_DYNAMIC_PROTOS) ${PROTO_GEN_DIR}/component/servo/v1/servo.grpc.pb.h ${PROTO_GEN_DIR}/component/servo/v1/servo.pb.cc ${PROTO_GEN_DIR}/component/servo/v1/servo.pb.h + ${PROTO_GEN_DIR}/component/switch/v1/switch.grpc.pb.cc + ${PROTO_GEN_DIR}/component/switch/v1/switch.grpc.pb.h + ${PROTO_GEN_DIR}/component/switch/v1/switch.pb.cc + ${PROTO_GEN_DIR}/component/switch/v1/switch.pb.h ${PROTO_GEN_DIR}/google/api/annotations.pb.cc ${PROTO_GEN_DIR}/google/api/annotations.pb.h ${PROTO_GEN_DIR}/google/api/httpbody.pb.cc @@ -245,6 +259,9 @@ if (VIAMCPPSDK_USE_DYNAMIC_PROTOS) COMMAND ${BUF_COMMAND} generate ${BUF_GOOGLE_API_SOURCE} --template buf.gen.yaml --path google/rpc --path google/api COMMAND ${BUF_COMMAND} generate ${BUF_VIAM_GOUTILS_SOURCE} --template buf.gen.yaml COMMAND ${BUF_COMMAND} generate ${BUF_VIAM_API_SOURCE} --template buf.gen.yaml --path ${BUF_PROTO_COMPONENTS_JOINED} + + # After generating the protos, include a step to invoke a search-and-replace for switch -> switch_ in the Switch component files + COMMAND ${CMAKE_COMMAND} "-DSWITCH_REPLACE_PATHS=\"${VIAMCPPSDK_SWITCH_REPLACE_PATHS}\"" -P ${CMAKE_CURRENT_SOURCE_DIR}/viamcppsdk_replace_switch.cmake MAIN_DEPENDENCY buf.gen.yaml ) @@ -328,6 +345,8 @@ target_sources(viamapi ${PROTO_GEN_DIR}/component/sensor/v1/sensor.pb.cc ${PROTO_GEN_DIR}/component/servo/v1/servo.grpc.pb.cc ${PROTO_GEN_DIR}/component/servo/v1/servo.pb.cc + ${PROTO_GEN_DIR}/component/switch/v1/switch.grpc.pb.cc + ${PROTO_GEN_DIR}/component/switch/v1/switch.pb.cc ${PROTO_GEN_DIR}/google/api/annotations.pb.cc ${PROTO_GEN_DIR}/google/api/http.pb.cc ${PROTO_GEN_DIR}/google/api/httpbody.pb.cc @@ -390,6 +409,8 @@ target_sources(viamapi ${PROTO_GEN_DIR}/../../viam/api/component/sensor/v1/sensor.pb.h ${PROTO_GEN_DIR}/../../viam/api/component/servo/v1/servo.grpc.pb.h ${PROTO_GEN_DIR}/../../viam/api/component/servo/v1/servo.pb.h + ${PROTO_GEN_DIR}/../../viam/api/component/switch/v1/switch.grpc.pb.h + ${PROTO_GEN_DIR}/../../viam/api/component/switch/v1/switch.pb.h ${PROTO_GEN_DIR}/../../viam/api/google/api/annotations.pb.h ${PROTO_GEN_DIR}/../../viam/api/google/api/http.pb.h ${PROTO_GEN_DIR}/../../viam/api/google/api/httpbody.pb.h diff --git a/src/viam/api/viamcppsdk_replace_switch.cmake b/src/viam/api/viamcppsdk_replace_switch.cmake new file mode 100644 index 000000000..1815faaa3 --- /dev/null +++ b/src/viam/api/viamcppsdk_replace_switch.cmake @@ -0,0 +1,10 @@ +if (NOT SWITCH_REPLACE_PATHS) + message(FATAL_ERROR "Please provide SWITCH_REPLACE_PATHS argument to switch replace script") +endif() + +foreach(SOURCE ${SWITCH_REPLACE_PATHS}) + file(READ "${SOURCE}" _src_text) + string(REPLACE "namespace switch " "namespace switch_ " _src_text "${_src_text}") + string(REPLACE "::switch::" "::switch_::" _src_text "${_src_text}") + file(WRITE "${SOURCE}" "${_src_text}") +endforeach() diff --git a/src/viam/sdk/CMakeLists.txt b/src/viam/sdk/CMakeLists.txt index 2af5eb483..94454f2ca 100644 --- a/src/viam/sdk/CMakeLists.txt +++ b/src/viam/sdk/CMakeLists.txt @@ -113,8 +113,11 @@ target_sources(viamsdk components/private/sensor_server.cpp components/private/servo_client.cpp components/private/servo_server.cpp + components/private/switch_client.cpp + components/private/switch_server.cpp components/sensor.cpp components/servo.cpp + components/switch.cpp config/resource.cpp module/handler_map.cpp module/module.cpp @@ -183,6 +186,7 @@ target_sources(viamsdk ../../viam/sdk/components/power_sensor.hpp ../../viam/sdk/components/sensor.hpp ../../viam/sdk/components/servo.hpp + ../../viam/sdk/components/switch.hpp ../../viam/sdk/config/resource.hpp ../../viam/sdk/module/handler_map.hpp ../../viam/sdk/module/module.hpp diff --git a/src/viam/sdk/components/private/switch_client.cpp b/src/viam/sdk/components/private/switch_client.cpp new file mode 100644 index 000000000..081f54277 --- /dev/null +++ b/src/viam/sdk/components/private/switch_client.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include + +#include + +namespace viam { +namespace sdk { +namespace impl { + +SwitchClient::SwitchClient(std::string name, std::shared_ptr channel) + : Switch(std::move(name)), + stub_(viam::component::switch_::v1::SwitchService::NewStub(channel)), + channel_(std::move(channel)) {} + +void SwitchClient::set_position(uint32_t position, const ProtoStruct& extra) { + make_client_helper(this, *stub_, &StubType::SetPosition) + .with(extra, [&](auto& request) { request.set_position(position); }) + .invoke(); +} + +uint32_t SwitchClient::get_position(const ProtoStruct& extra) { + return make_client_helper(this, *stub_, &StubType::GetPosition) + .with(extra) + .invoke([](auto& response) { return response.position(); }); +} + +uint32_t SwitchClient::get_number_of_positions(const ProtoStruct& extra) { + return make_client_helper(this, *stub_, &StubType::GetNumberOfPositions) + .with(extra) + .invoke([](auto& response) { return response.number_of_positions(); }); +} + +ProtoStruct SwitchClient::do_command(const ProtoStruct& command) { + return make_client_helper(this, *stub_, &StubType::DoCommand) + .with([&](auto& request) { *request.mutable_command() = to_proto(command); }) + .invoke([](auto& response) { return from_proto(response.result()); }); +} + +} // namespace impl +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/components/private/switch_client.hpp b/src/viam/sdk/components/private/switch_client.hpp new file mode 100644 index 000000000..ce93b1200 --- /dev/null +++ b/src/viam/sdk/components/private/switch_client.hpp @@ -0,0 +1,43 @@ +/// @file components/private/switch_client.hpp +/// +/// @brief Implements a gRPC client for the `Switch` component +#pragma once + +#include + +#include + +#include + +namespace viam { +namespace sdk { +namespace impl { + +/// @class SwitchClient +/// @brief gRPC client implementation of a `Switch` component. +/// @ingroup Switch +class SwitchClient : public Switch { + public: + using interface_type = Switch; + SwitchClient(std::string name, std::shared_ptr channel); + + void set_position(uint32_t position, const ProtoStruct& extra) override; + uint32_t get_position(const ProtoStruct& extra) override; + uint32_t get_number_of_positions(const ProtoStruct& extra) override; + ProtoStruct do_command(const ProtoStruct& command) override; + + // Using declarations to introduce convenience overloads of interface which do not need to be + // passed the ProtoStruct parameter. + using Switch::get_number_of_positions; + using Switch::get_position; + using Switch::set_position; + + private: + using StubType = viam::component::switch_::v1::SwitchService::StubInterface; + std::unique_ptr stub_; + std::shared_ptr channel_; +}; + +} // namespace impl +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/components/private/switch_server.cpp b/src/viam/sdk/components/private/switch_server.cpp new file mode 100644 index 000000000..cd8262ee8 --- /dev/null +++ b/src/viam/sdk/components/private/switch_server.cpp @@ -0,0 +1,54 @@ +#include + +#include + +namespace viam { +namespace sdk { +namespace impl { + +SwitchServer::SwitchServer(std::shared_ptr manager) + : ResourceServer(std::move(manager)) {} + +::grpc::Status SwitchServer::SetPosition( + ::grpc::ServerContext*, + const ::viam::component::switch_::v1::SetPositionRequest* request, + ::viam::component::switch_::v1::SetPositionResponse*) noexcept { + return make_service_helper( + "SwitchServer::SetPosition", this, request)([&](auto& helper, auto& switch_) { + switch_->set_position(request->position(), helper.getExtra()); + }); +} + +::grpc::Status SwitchServer::GetPosition( + ::grpc::ServerContext*, + const ::viam::component::switch_::v1::GetPositionRequest* request, + ::viam::component::switch_::v1::GetPositionResponse* response) noexcept { + return make_service_helper( + "SwitchServer::GetPosition", this, request)([&](auto& helper, auto& switch_) { + response->set_position(switch_->get_position(helper.getExtra())); + }); +} + +::grpc::Status SwitchServer::GetNumberOfPositions( + ::grpc::ServerContext*, + const ::viam::component::switch_::v1::GetNumberOfPositionsRequest* request, + ::viam::component::switch_::v1::GetNumberOfPositionsResponse* response) noexcept { + return make_service_helper( + "SwitchServer::GetNumberOfPositions", this, request)([&](auto& helper, auto& switch_) { + response->set_number_of_positions(switch_->get_number_of_positions(helper.getExtra())); + }); +} + +::grpc::Status SwitchServer::DoCommand(::grpc::ServerContext*, + const ::viam::common::v1::DoCommandRequest* request, + ::viam::common::v1::DoCommandResponse* response) noexcept { + return make_service_helper( + "SwitchServer::DoCommand", this, request)([&](auto&, auto& switch_) { + const ProtoStruct result = switch_->do_command(from_proto(request->command())); + *response->mutable_result() = to_proto(result); + }); +} + +} // namespace impl +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/components/private/switch_server.hpp b/src/viam/sdk/components/private/switch_server.hpp new file mode 100644 index 000000000..dbf9691d3 --- /dev/null +++ b/src/viam/sdk/components/private/switch_server.hpp @@ -0,0 +1,50 @@ +/// @file components/private/switch_server.hpp +/// +/// @brief Implements a gRPC server for the `Switch` component +#pragma once + +#include +#include + +#include +#include +#include + +namespace viam { +namespace sdk { +namespace impl { + +/// @class SwitchServer +/// @brief gRPC server implementation of a `Switch` component. +/// @ingroup Switch +class SwitchServer : public ResourceServer, + public viam::component::switch_::v1::SwitchService::Service { + public: + using interface_type = Switch; + using service_type = component::switch_::v1::SwitchService; + + explicit SwitchServer(std::shared_ptr manager); + + ::grpc::Status SetPosition( + ::grpc::ServerContext* context, + const ::viam::component::switch_::v1::SetPositionRequest* request, + ::viam::component::switch_::v1::SetPositionResponse* response) noexcept override; + + ::grpc::Status GetPosition( + ::grpc::ServerContext* context, + const ::viam::component::switch_::v1::GetPositionRequest* request, + ::viam::component::switch_::v1::GetPositionResponse* response) noexcept override; + + ::grpc::Status GetNumberOfPositions( + ::grpc::ServerContext* context, + const ::viam::component::switch_::v1::GetNumberOfPositionsRequest* request, + ::viam::component::switch_::v1::GetNumberOfPositionsResponse* response) noexcept override; + + ::grpc::Status DoCommand(::grpc::ServerContext* context, + const ::viam::common::v1::DoCommandRequest* request, + ::viam::common::v1::DoCommandResponse* response) noexcept override; +}; + +} // namespace impl +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/components/switch.cpp b/src/viam/sdk/components/switch.cpp new file mode 100644 index 000000000..4c0a199cb --- /dev/null +++ b/src/viam/sdk/components/switch.cpp @@ -0,0 +1,19 @@ +#include + +#include + +namespace viam { +namespace sdk { + +API Switch::api() const { + return API::get(); +} + +API API::traits::api() { + return {kRDK, kComponent, "switch"}; +} + +Switch::Switch(std::string name) : Component(std::move(name)) {} + +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/components/switch.hpp b/src/viam/sdk/components/switch.hpp new file mode 100644 index 000000000..453e99446 --- /dev/null +++ b/src/viam/sdk/components/switch.hpp @@ -0,0 +1,77 @@ +/// @file components/switch.hpp +/// +/// @brief Defines a `Switch` component +#pragma once + +#include + +#include + +#include +#include +#include + +namespace viam { +namespace sdk { + +/// @defgroup Switch Classes related to the Switch component. + +/// @class Switch switch.hpp "components/switch.hpp" +/// @brief A `Switch` represents a physical switch with multiple positions. +/// @ingroup Switch +/// +/// This acts as an abstract parent class to be inherited from by any drivers representing +/// specific switch implementations. This class cannot be used on its own. +class Switch : public Component { + public: + /// @brief Set the position of the switch. + /// @param position The position to set the switch to. + inline void set_position(uint32_t position) { + set_position(position, {}); + } + + /// @brief Set the position of the switch. + /// @param position The position to set the switch to. + /// @param extra Any additional arguments to the method. + virtual void set_position(uint32_t position, const ProtoStruct& extra) = 0; + + /// @brief Get the current position of the switch. + /// @return The current position of the switch. + inline uint32_t get_position() { + return get_position({}); + } + + /// @brief Get the current position of the switch. + /// @param extra Any additional arguments to the method. + /// @return The current position of the switch. + virtual uint32_t get_position(const ProtoStruct& extra) = 0; + + /// @brief Get the number of positions that the switch supports. + /// @return The number of positions that the switch supports. + inline uint32_t get_number_of_positions() { + return get_number_of_positions({}); + } + + /// @brief Get the number of positions that the switch supports. + /// @param extra Any additional arguments to the method. + /// @return The number of positions that the switch supports. + virtual uint32_t get_number_of_positions(const ProtoStruct& extra) = 0; + + /// @brief Send/receive arbitrary commands to the resource. + /// @param command The command to execute. + /// @return The result of the executed command. + virtual ProtoStruct do_command(const ProtoStruct& command) = 0; + + API api() const override; + + protected: + explicit Switch(std::string name); +}; + +template <> +struct API::traits { + static API api(); +}; + +} // namespace sdk +} // namespace viam diff --git a/src/viam/sdk/registry/registry.cpp b/src/viam/sdk/registry/registry.cpp index 0943902f2..90cfe9af2 100644 --- a/src/viam/sdk/registry/registry.cpp +++ b/src/viam/sdk/registry/registry.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include #include @@ -214,6 +216,7 @@ void Registry::register_resources() { register_resource(); register_resource(); register_resource(); + register_resource(); // Register all services register_resource(); diff --git a/src/viam/sdk/tests/CMakeLists.txt b/src/viam/sdk/tests/CMakeLists.txt index 605599f4a..29e21180c 100644 --- a/src/viam/sdk/tests/CMakeLists.txt +++ b/src/viam/sdk/tests/CMakeLists.txt @@ -37,6 +37,7 @@ target_sources(viamsdk_test mocks/mock_power_sensor.cpp mocks/mock_sensor.cpp mocks/mock_servo.cpp + mocks/mock_switch.cpp mocks/mock_robot.cpp test_utils.cpp ) @@ -76,4 +77,5 @@ viamcppsdk_add_boost_test(test_proto_value_visit.cpp) viamcppsdk_add_boost_test(test_resource.cpp) viamcppsdk_add_boost_test(test_sensor.cpp) viamcppsdk_add_boost_test(test_servo.cpp) +viamcppsdk_add_boost_test(test_switch.cpp) viamcppsdk_add_boost_test(test_robot.cpp) diff --git a/src/viam/sdk/tests/mocks/mock_switch.cpp b/src/viam/sdk/tests/mocks/mock_switch.cpp new file mode 100644 index 000000000..fa3fb7934 --- /dev/null +++ b/src/viam/sdk/tests/mocks/mock_switch.cpp @@ -0,0 +1,31 @@ +#include + +#include + +namespace viam { +namespace sdktests { +namespace switch_ { + +std::shared_ptr MockSwitch::get_mock_switch() { + return std::make_shared("mock_switch"); +} + +void MockSwitch::set_position(uint32_t position, const sdk::ProtoStruct&) { + current_position = position; +} + +uint32_t MockSwitch::get_position(const sdk::ProtoStruct&) { + return current_position; +} + +uint32_t MockSwitch::get_number_of_positions(const sdk::ProtoStruct&) { + return number_of_positions; +} + +sdk::ProtoStruct MockSwitch::do_command(const sdk::ProtoStruct& command) { + return (peek_command = command); +} + +} // namespace switch_ +} // namespace sdktests +} // namespace viam diff --git a/src/viam/sdk/tests/mocks/mock_switch.hpp b/src/viam/sdk/tests/mocks/mock_switch.hpp new file mode 100644 index 000000000..124406ce4 --- /dev/null +++ b/src/viam/sdk/tests/mocks/mock_switch.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace viam { +namespace sdktests { +namespace switch_ { + +class MockSwitch : public sdk::Switch { + public: + MockSwitch(std::string name) : Switch(std::move(name)) {} + + static std::shared_ptr get_mock_switch(); + + void set_position(uint32_t position, const sdk::ProtoStruct&) override; + uint32_t get_position(const sdk::ProtoStruct&) override; + uint32_t get_number_of_positions(const sdk::ProtoStruct&) override; + sdk::ProtoStruct do_command(const sdk::ProtoStruct& command) override; + + uint32_t current_position; + uint32_t number_of_positions; + sdk::ProtoStruct peek_command; +}; + +} // namespace switch_ +} // namespace sdktests +} // namespace viam diff --git a/src/viam/sdk/tests/test_switch.cpp b/src/viam/sdk/tests/test_switch.cpp new file mode 100644 index 000000000..a99479678 --- /dev/null +++ b/src/viam/sdk/tests/test_switch.cpp @@ -0,0 +1,62 @@ +#define BOOST_TEST_MODULE test module test_switch +#include + +#include + +#include +#include + +BOOST_TEST_DONT_PRINT_LOG_VALUE(viam::sdk::ProtoStruct) + +namespace viam { +namespace sdktests { + +using namespace switch_; + +using namespace viam::sdk; + +BOOST_AUTO_TEST_SUITE(test_switch) + +BOOST_AUTO_TEST_CASE(mock_get_api) { + const MockSwitch switch_("mock_switch"); + auto api = switch_.api(); + auto static_api = API::get(); + + BOOST_CHECK_EQUAL(api, static_api); + BOOST_CHECK_EQUAL(static_api.resource_subtype(), "switch"); +} + +BOOST_AUTO_TEST_CASE(test_positions) { + std::shared_ptr mock = MockSwitch::get_mock_switch(); + client_to_mock_pipeline(mock, [&](Switch& client) { + uint32_t position = 3; + client.set_position(position); + BOOST_CHECK_EQUAL(mock->current_position, position); + BOOST_CHECK_EQUAL(client.get_position(), position); + }); +} + +BOOST_AUTO_TEST_CASE(test_number_of_positions) { + std::shared_ptr mock = MockSwitch::get_mock_switch(); + client_to_mock_pipeline(mock, [&](Switch& client) { + mock->number_of_positions = 5; + BOOST_CHECK_EQUAL(client.get_number_of_positions(), 5); + }); +} + +BOOST_AUTO_TEST_CASE(test_do_command) { + std::shared_ptr mock = MockSwitch::get_mock_switch(); + client_to_mock_pipeline(mock, [](Switch& client) { + ProtoStruct expected = fake_map(); + + ProtoStruct command = fake_map(); + ProtoStruct result_map = client.do_command(command); + + BOOST_CHECK(result_map.at("test") == expected.at("test")); + }); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace sdktests +} // namespace viam