Skip to content

Draft: RSDK-10311: Switch component #391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/viam/api/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions src/viam/api/viamcppsdk_replace_switch.cmake
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 4 additions & 0 deletions src/viam/sdk/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions src/viam/sdk/components/private/switch_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <viam/sdk/components/private/switch_client.hpp>

#include <viam/api/component/switch/v1/switch.grpc.pb.h>
#include <viam/api/component/switch/v1/switch.pb.h>

#include <viam/sdk/common/client_helper.hpp>

namespace viam {
namespace sdk {
namespace impl {

SwitchClient::SwitchClient(std::string name, std::shared_ptr<grpc::Channel> 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
43 changes: 43 additions & 0 deletions src/viam/sdk/components/private/switch_client.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/// @file components/private/switch_client.hpp
///
/// @brief Implements a gRPC client for the `Switch` component
#pragma once

#include <grpcpp/channel.h>

#include <viam/api/component/switch/v1/switch.grpc.pb.h>

#include <viam/sdk/components/switch.hpp>

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<grpc::Channel> 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<StubType> stub_;
std::shared_ptr<grpc::Channel> channel_;
};

} // namespace impl
} // namespace sdk
} // namespace viam
54 changes: 54 additions & 0 deletions src/viam/sdk/components/private/switch_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <viam/sdk/components/private/switch_server.hpp>

#include <viam/sdk/common/private/service_helper.hpp>

namespace viam {
namespace sdk {
namespace impl {

SwitchServer::SwitchServer(std::shared_ptr<ResourceManager> 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<Switch>(
"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<Switch>(
"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<Switch>(
"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<Switch>(
"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
50 changes: 50 additions & 0 deletions src/viam/sdk/components/private/switch_server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/// @file components/private/switch_server.hpp
///
/// @brief Implements a gRPC server for the `Switch` component
#pragma once

#include <viam/api/component/switch/v1/switch.grpc.pb.h>
#include <viam/api/component/switch/v1/switch.pb.h>

#include <viam/sdk/components/switch.hpp>
#include <viam/sdk/resource/resource_manager.hpp>
#include <viam/sdk/resource/resource_server_base.hpp>

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<ResourceManager> 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
19 changes: 19 additions & 0 deletions src/viam/sdk/components/switch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <viam/sdk/components/switch.hpp>

#include <viam/sdk/common/utils.hpp>

namespace viam {
namespace sdk {

API Switch::api() const {
return API::get<Switch>();
}

API API::traits<Switch>::api() {
return {kRDK, kComponent, "switch"};
}

Switch::Switch(std::string name) : Component(std::move(name)) {}

} // namespace sdk
} // namespace viam
77 changes: 77 additions & 0 deletions src/viam/sdk/components/switch.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/// @file components/switch.hpp
///
/// @brief Defines a `Switch` component
#pragma once

#include <cstdint>

#include <string>

#include <viam/sdk/common/proto_value.hpp>
#include <viam/sdk/components/component.hpp>
#include <viam/sdk/resource/resource_api.hpp>

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<Switch> {
static API api();
};

} // namespace sdk
} // namespace viam
3 changes: 3 additions & 0 deletions src/viam/sdk/registry/registry.cpp
Original file line number Diff line number Diff line change
@@ -43,6 +43,8 @@
#include <viam/sdk/components/private/sensor_server.hpp>
#include <viam/sdk/components/private/servo_client.hpp>
#include <viam/sdk/components/private/servo_server.hpp>
#include <viam/sdk/components/private/switch_client.hpp>
#include <viam/sdk/components/private/switch_server.hpp>
#include <viam/sdk/resource/resource.hpp>
#include <viam/sdk/resource/resource_api.hpp>
#include <viam/sdk/services/private/discovery_client.hpp>
@@ -214,6 +216,7 @@ void Registry::register_resources() {
register_resource<impl::PowerSensorClient, impl::PowerSensorServer>();
register_resource<impl::SensorClient, impl::SensorServer>();
register_resource<impl::ServoClient, impl::ServoServer>();
register_resource<impl::SwitchClient, impl::SwitchServer>();

// Register all services
register_resource<impl::DiscoveryClient, impl::DiscoveryServer>();
2 changes: 2 additions & 0 deletions src/viam/sdk/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
31 changes: 31 additions & 0 deletions src/viam/sdk/tests/mocks/mock_switch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <viam/sdk/tests/mocks/mock_switch.hpp>

#include <viam/sdk/tests/test_utils.hpp>

namespace viam {
namespace sdktests {
namespace switch_ {

std::shared_ptr<MockSwitch> MockSwitch::get_mock_switch() {
return std::make_shared<MockSwitch>("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
27 changes: 27 additions & 0 deletions src/viam/sdk/tests/mocks/mock_switch.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <viam/sdk/components/switch.hpp>

namespace viam {
namespace sdktests {
namespace switch_ {

class MockSwitch : public sdk::Switch {
public:
MockSwitch(std::string name) : Switch(std::move(name)) {}

static std::shared_ptr<MockSwitch> 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
62 changes: 62 additions & 0 deletions src/viam/sdk/tests/test_switch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#define BOOST_TEST_MODULE test module test_switch
#include <viam/sdk/components/switch.hpp>

#include <boost/test/included/unit_test.hpp>

#include <viam/sdk/tests/mocks/mock_switch.hpp>
#include <viam/sdk/tests/test_utils.hpp>

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<Switch>();

BOOST_CHECK_EQUAL(api, static_api);
BOOST_CHECK_EQUAL(static_api.resource_subtype(), "switch");
}

BOOST_AUTO_TEST_CASE(test_positions) {
std::shared_ptr<MockSwitch> mock = MockSwitch::get_mock_switch();
client_to_mock_pipeline<Switch>(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<MockSwitch> mock = MockSwitch::get_mock_switch();
client_to_mock_pipeline<Switch>(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<MockSwitch> mock = MockSwitch::get_mock_switch();
client_to_mock_pipeline<Switch>(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