Skip to content

Commit 9dad912

Browse files
committed
WIP
Signed-off-by: Michel Hidalgo <[email protected]>
1 parent e34b68d commit 9dad912

24 files changed

+1178
-59
lines changed

spot_driver/CMakeLists.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ if(CCACHE_PROGRAM)
1515
endif()
1616

1717
set(THIS_PACKAGE_INCLUDE_ROS_DEPENDS
18+
bondcpp
1819
bosdyn_api_msgs
1920
bosdyn_cmake_module
2021
bosdyn_spot_api_msgs
@@ -50,6 +51,7 @@ find_package(OpenCV 4 REQUIRED)
5051
add_library(spot_api
5152
src/api/default_kinematic_api.cpp
5253
src/api/default_image_client.cpp
54+
src/api/default_lease_client.cpp
5355
src/api/default_spot_api.cpp
5456
src/api/default_state_client.cpp
5557
src/api/default_time_sync_api.cpp
@@ -71,6 +73,9 @@ add_library(spot_api
7173
src/interfaces/rclcpp_tf_broadcaster_interface.cpp
7274
src/interfaces/rclcpp_tf_listener_interface.cpp
7375
src/interfaces/rclcpp_wall_timer_interface.cpp
76+
src/lease/lease_manager.cpp
77+
src/lease/lease_manager_node.cpp
78+
src/lease/lease_middleware_handle.cpp
7479
src/kinematic/kinematic_node.cpp
7580
src/kinematic/kinematic_service.cpp
7681
src/kinematic/kinematic_middleware_handle.cpp
@@ -145,6 +150,34 @@ rclcpp_components_register_node(
145150
PLUGIN "spot_ros2::StatePublisherNode"
146151
EXECUTABLE state_publisher_node_component)
147152

153+
###
154+
# Spot lease manager
155+
###
156+
157+
# Create executable to allow running LeaseManagerNode directly as a ROS 2 node
158+
add_executable(lease_manager_node src/lease/lease_manager_node_main.cpp)
159+
target_link_libraries(lease_manager_node PUBLIC spot_api)
160+
target_include_directories(lease_manager_node
161+
PUBLIC
162+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
163+
$<INSTALL_INTERFACE:include>
164+
)
165+
166+
# Register a composable node to allow loading StatePublisherNode in a component container
167+
add_library(lease_manager_component SHARED src/lease/lease_manager_component.cpp)
168+
target_include_directories(lease_manager_component
169+
PUBLIC
170+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
171+
$<INSTALL_INTERFACE:include>
172+
)
173+
target_link_libraries(lease_manager_component PUBLIC spot_api)
174+
ament_target_dependencies(lease_manager_component PUBLIC rclcpp_components)
175+
176+
rclcpp_components_register_node(
177+
lease_manager_component
178+
PLUGIN "spot_ros2::LeaseManagerNode"
179+
EXECUTABLE lease_manager_node_component)
180+
148181
###
149182
# Spot IK
150183
###
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2025 Boston Dynamics AI Institute LLC. All rights reserved.
2+
3+
#pragma once
4+
5+
#include <bosdyn/client/lease/lease.h>
6+
#include <bosdyn/client/lease/lease_client.h>
7+
#include <bosdyn/client/lease/lease_keepalive.h>
8+
#include <bosdyn/client/lease/lease_wallet.h>
9+
#include <bosdyn/client/sdk/client_sdk.h>
10+
#include <spot_driver/api/lease_client_interface.hpp>
11+
12+
#include <memory>
13+
#include <string>
14+
#include <unordered_map>
15+
16+
namespace spot_ros2 {
17+
/**
18+
* @brief Implements LeaseClientInterface to use the Spot C++ Lease Client.
19+
*/
20+
class DefaultLeaseClient : public LeaseClientInterface {
21+
public:
22+
explicit DefaultLeaseClient(::bosdyn::client::LeaseClient* lease_client);
23+
24+
[[nodiscard]] tl::expected<::bosdyn::client::Lease, std::string> acquireLease(
25+
const std::string& resource_name, LeaseUseCallback retention_failure_callback) override;
26+
27+
[[nodiscard]] tl::expected<::bosdyn::client::Lease, std::string> takeLease(
28+
const std::string& resource_name, LeaseUseCallback retention_failure_callback) override;
29+
30+
[[nodiscard]] tl::expected<bool, std::string> returnLease(const ::bosdyn::client::Lease& lease) override;
31+
32+
[[nodiscard]] std::shared_ptr<::bosdyn::client::LeaseWallet> getLeaseWallet() const override;
33+
34+
[[nodiscard]] const ::bosdyn::client::ResourceHierarchy& getResourceHierarchy() const override;
35+
36+
private:
37+
::bosdyn::client::LeaseClient* lease_client_;
38+
std::unordered_map<std::string, std::unique_ptr<::bosdyn::client::LeaseKeepAlive>> keepalives_;
39+
std::mutex keepalives_mutex_;
40+
};
41+
} // namespace spot_ros2

spot_driver/include/spot_driver/api/default_spot_api.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <bosdyn/client/robot/robot.h>
88
#include <bosdyn/client/sdk/client_sdk.h>
99

10+
#include <spot_driver/api/lease_client_interface.hpp>
1011
#include <spot_driver/api/state_client_interface.hpp>
1112
#include <spot_driver/api/time_sync_api.hpp>
1213
#include <spot_driver/api/world_object_client_interface.hpp>
@@ -32,6 +33,7 @@ class DefaultSpotApi : public SpotApi {
3233
[[nodiscard]] std::shared_ptr<KinematicApi> kinematicInterface() const override;
3334
[[nodiscard]] std::shared_ptr<ImageClientInterface> image_client_interface() const override;
3435
[[nodiscard]] std::shared_ptr<StateClientInterface> stateClientInterface() const override;
36+
[[nodiscard]] std::shared_ptr<LeaseClientInterface> leaseClientInterface() const override;
3537
[[nodiscard]] std::shared_ptr<TimeSyncApi> timeSyncInterface() const override;
3638
[[nodiscard]] std::shared_ptr<WorldObjectClientInterface> worldObjectClientInterface() const override;
3739

@@ -41,6 +43,7 @@ class DefaultSpotApi : public SpotApi {
4143
std::shared_ptr<KinematicApi> kinematic_interface_;
4244
std::shared_ptr<ImageClientInterface> image_client_interface_;
4345
std::shared_ptr<StateClientInterface> state_client_interface_;
46+
std::shared_ptr<LeaseClientInterface> lease_client_interface_;
4447
std::shared_ptr<TimeSyncApi> time_sync_api_;
4548
std::shared_ptr<WorldObjectClientInterface> world_object_client_interface_;
4649
std::string robot_name_;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2025 Boston Dynamics AI Institute LLC. All rights reserved.
2+
3+
#pragma once
4+
5+
#include <functional>
6+
#include <memory>
7+
#include <string>
8+
9+
#include <tl_expected/expected.hpp>
10+
11+
#include <bosdyn/api/lease.pb.h>
12+
#include <bosdyn/client/lease/lease.h>
13+
#include <bosdyn/client/lease/lease_wallet.h>
14+
15+
namespace spot_ros2 {
16+
class LeaseClientInterface {
17+
public:
18+
using LeaseUseCallback = std::function<void(const bosdyn::api::LeaseUseResult&)>;
19+
20+
// LeaseClientInterface is move-only
21+
LeaseClientInterface() = default;
22+
LeaseClientInterface(LeaseClientInterface&& other) = default;
23+
LeaseClientInterface(const LeaseClientInterface&) = delete;
24+
LeaseClientInterface& operator=(LeaseClientInterface&& other) = default;
25+
LeaseClientInterface& operator=(const LeaseClientInterface&) = delete;
26+
27+
virtual ~LeaseClientInterface() = default;
28+
29+
virtual tl::expected<::bosdyn::client::Lease, std::string> acquireLease(
30+
const std::string& resource_name, LeaseUseCallback retention_failure_callback) = 0;
31+
32+
virtual tl::expected<::bosdyn::client::Lease, std::string> takeLease(
33+
const std::string& resource_name, LeaseUseCallback retention_failure_callback) = 0;
34+
35+
virtual tl::expected<bool, std::string> returnLease(const ::bosdyn::client::Lease& lease) = 0;
36+
37+
virtual std::shared_ptr<::bosdyn::client::LeaseWallet> getLeaseWallet() const = 0;
38+
39+
virtual const ::bosdyn::client::ResourceHierarchy& getResourceHierarchy() const = 0;
40+
};
41+
} // namespace spot_ros2

spot_driver/include/spot_driver/api/spot_api.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#pragma once
44

55
#include <spot_driver/api/kinematic_api.hpp>
6+
#include <spot_driver/api/lease_client_interface.hpp>
67
#include <spot_driver/api/state_client_interface.hpp>
78
#include <spot_driver/api/time_sync_api.hpp>
89
#include <spot_driver/api/world_object_client_interface.hpp>
@@ -44,6 +45,7 @@ class SpotApi {
4445
* @return A shared_ptr to an instance of StateClientInterface which is owned by this object.
4546
*/
4647
virtual std::shared_ptr<StateClientInterface> stateClientInterface() const = 0;
48+
[[nodiscard]] virtual std::shared_ptr<LeaseClientInterface> leaseClientInterface() const = 0;
4749
virtual std::shared_ptr<TimeSyncApi> timeSyncInterface() const = 0;
4850
[[nodiscard]] virtual std::shared_ptr<WorldObjectClientInterface> worldObjectClientInterface() const = 0;
4951
};

spot_driver/include/spot_driver/lease/lease_manager.hpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
#pragma once
44

5-
#include <spot_driver/api/kinematic_api.hpp>
6-
5+
#include <spot_driver/api/lease_client_interface.hpp>
76
#include <spot_driver/interfaces/logger_interface_base.hpp>
87

9-
#include <spot_msgs/srv/acquire_sublease.hpp>
8+
#include <spot_msgs/srv/acquire_lease.hpp>
9+
#include <spot_msgs/srv/return_lease.hpp>
1010

1111
#include <memory>
1212
#include <string>
@@ -25,16 +25,20 @@ class LeaseManager {
2525
*/
2626
class MiddlewareHandle {
2727
public:
28+
class Bond {
29+
public:
30+
virtual ~Bond() = default;
31+
};
32+
2833
virtual void createAcquireLeaseService(const std::string& service_name,
2934
std::function<void(const std::shared_ptr<AcquireLease::Request>,
3035
std::shared_ptr<AcquireLease::Response>)> callback) = 0;
3136

3237
virtual void createReturnLeaseService(const std::string& service_name,
3338
std::function<void(const std::shared_ptr<ReturnLease::Request>,
3439
std::shared_ptr<ReturnLease::Response>)> callback) = 0;
35-
virtual void createBond(const std::string& node_name) = 0;
3640

37-
virtual void destroyBond(const std::string& node_name) = 0;
41+
virtual std::shared_ptr<Bond> createBond(const std::string& node_name, std::function<void()> break_callback) = 0;
3842

3943
virtual ~MiddlewareHandle() = default;
4044
};
@@ -45,9 +49,9 @@ class LeaseManager {
4549
* @param kinematic_api The Api to interact with the Spot SDK.
4650
* @param logger Logging interface.
4751
*/
48-
explicit LeaseManager(std::shared_ptr<LeaseApi> lease_api,
49-
std::shared_ptr<LoggerInterfaceBase> logger,
50-
std::unique_ptr<MiddlewareHandle> middleware_handle);
52+
explicit LeaseManager(std::shared_ptr<LeaseClientInterface> lease_client,
53+
std::shared_ptr<LoggerInterfaceBase> logger,
54+
std::unique_ptr<MiddlewareHandle> middleware_handle);
5155

5256
/** Initialize the service. */
5357
void initialize();
@@ -60,19 +64,26 @@ class LeaseManager {
6064
void acquireLease(const std::shared_ptr<AcquireLease::Request> request,
6165
std::shared_ptr<AcquireLease::Response> response);
6266

63-
void returnLease(const std::shared_ptr<ReleaseLease::Request> request,
64-
std::shared_ptr<ReleaseLease::Response> response);
67+
void returnLease(const std::shared_ptr<ReturnLease::Request> request,
68+
std::shared_ptr<ReturnLease::Response> response);
6569

6670
private:
71+
void onLeaseRetentionFailure(const bosdyn::api::LeaseUseResult& result);
72+
6773
// The API to interact with Spot SDK.
68-
std::shared_ptr<LeaseApi> lease_api_;
74+
std::shared_ptr<LeaseClientInterface> lease_client_;
6975

7076
// Logger.
7177
std::shared_ptr<LoggerInterfaceBase> logger_;
7278

7379
// The service provider.
7480
std::unique_ptr<MiddlewareHandle> middleware_handle_;
7581

76-
std::unordered_map<std::string, std::string>
82+
struct ManagedSublease {
83+
bosdyn::client::Lease lease;
84+
std::shared_ptr<MiddlewareHandle::Bond> bond;
85+
};
86+
std::unordered_map<std::string, ManagedSublease> subleases_;
87+
std::recursive_mutex subleases_mutex_;
7788
};
7889
} // namespace spot_ros2::lease
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) 2025 Boston Dynamics AI Institute LLC. All rights reserved.
2+
3+
#pragma once
4+
5+
#include <spot_driver/lease/lease_manager.hpp>
6+
7+
#include <spot_driver/api/spot_api.hpp>
8+
#include <spot_driver/interfaces/logger_interface_base.hpp>
9+
#include <spot_driver/interfaces/parameter_interface_base.hpp>
10+
11+
#include <rclcpp/node.hpp>
12+
#include <rclcpp/node_interfaces/node_base_interface.hpp>
13+
#include <rclcpp/node_options.hpp>
14+
15+
#include <memory>
16+
17+
namespace spot_ros2::lease {
18+
class LeaseManagerNode {
19+
public:
20+
explicit LeaseManagerNode(std::shared_ptr<rclcpp::Node> node, std::unique_ptr<SpotApi> spot_api,
21+
std::shared_ptr<ParameterInterfaceBase> parameter_interface,
22+
std::shared_ptr<LoggerInterfaceBase> logger_interface);
23+
24+
explicit LeaseManagerNode(const rclcpp::NodeOptions& node_options = rclcpp::NodeOptions{});
25+
26+
/**
27+
* @brief Returns the NodeBaseInterface of this class's node.
28+
* @details This function exists to allow spinning the class's node as if it were derived from rclcpp::Node.
29+
* This allows loading this class as a component node in a composable node container without requiring that it inherit
30+
* from rclcpp::Node.
31+
*
32+
* @return A shared_ptr to the NodeBaseInterface of the node stored as a private member of this class.
33+
*/
34+
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> get_node_base_interface();
35+
36+
private:
37+
std::shared_ptr<rclcpp::Node> node_;
38+
std::unique_ptr<SpotApi> spot_api_;
39+
std::unique_ptr<LeaseManager> internal_;
40+
41+
void initialize(std::shared_ptr<rclcpp::Node> node, std::unique_ptr<SpotApi> spot_api,
42+
std::shared_ptr<ParameterInterfaceBase> parameter_interface,
43+
const std::shared_ptr<LoggerInterfaceBase> logger_interface);
44+
};
45+
} // namespace spot_ros2::kinematic
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2025 Boston Dynamics AI Institute LLC. All rights reserved.
2+
3+
#pragma once
4+
5+
#include <spot_driver/lease/lease_manager.hpp>
6+
7+
#include <functional>
8+
#include <memory>
9+
#include <string>
10+
11+
#include <rclcpp/node.hpp>
12+
#include <rclcpp/service.hpp>
13+
14+
namespace spot_ros2::lease {
15+
16+
class LeaseMiddlewareHandle : public LeaseManager::MiddlewareHandle {
17+
public:
18+
explicit LeaseMiddlewareHandle(std::shared_ptr<rclcpp::Node> node);
19+
20+
void createAcquireLeaseService(
21+
const std::string& service_name,
22+
std::function<void(const std::shared_ptr<AcquireLease::Request>,
23+
std::shared_ptr<AcquireLease::Response>)> callback) override;
24+
25+
void createReturnLeaseService(
26+
const std::string& service_name,
27+
std::function<void(const std::shared_ptr<ReturnLease::Request>,
28+
std::shared_ptr<ReturnLease::Response>)> callback) override;
29+
30+
std::shared_ptr<LeaseManager::MiddlewareHandle::Bond> createBond(
31+
const std::string& node_name, std::function<void()> break_callback) override;
32+
33+
private:
34+
std::shared_ptr<rclcpp::Node> node_;
35+
std::shared_ptr<rclcpp::Service<AcquireLease>> acquire_lease_service_;
36+
std::shared_ptr<rclcpp::Service<ReturnLease>> return_lease_service_;
37+
};
38+
} // namespace spot_ros2::lease

spot_driver/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<buildtool_depend>ament_cmake_python</buildtool_depend>
1212
<buildtool_depend>rosidl_default_generators</buildtool_depend>
1313

14+
<depend>bondcpp</depend>
1415
<depend>bosdyn</depend>
1516
<depend>bosdyn_cmake_module</depend>
1617
<depend>bosdyn_msgs</depend>

0 commit comments

Comments
 (0)