Fabric 0.2.0
Loading...
Searching...
No Matches
capability_client.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <chrono>
4#include <mutex>
5#include <condition_variable>
6
7#include <bondcpp/bond.hpp>
8#include <rclcpp/rclcpp.hpp>
9
13
14#include <capabilities2_msgs/srv/establish_bond.hpp>
15#include <capabilities2_msgs/srv/get_interfaces.hpp>
16#include <capabilities2_msgs/srv/get_semantic_interfaces.hpp>
17#include <capabilities2_msgs/srv/get_providers.hpp>
18#include <capabilities2_msgs/srv/use_capability.hpp>
19#include <capabilities2_msgs/srv/free_capability.hpp>
20#include <capabilities2_msgs/srv/connect_capability.hpp>
21#include <capabilities2_msgs/srv/trigger_capability.hpp>
22
23#include <capabilities2_msgs/msg/capability_event_code.hpp>
24
25namespace fabric
26{
27
33{
34public:
35 using GetInterfaces = capabilities2_msgs::srv::GetInterfaces;
36 using GetSemanticInterfaces = capabilities2_msgs::srv::GetSemanticInterfaces;
37 using GetProviders = capabilities2_msgs::srv::GetProviders;
38 using EstablishBond = capabilities2_msgs::srv::EstablishBond;
39 using UseCapability = capabilities2_msgs::srv::UseCapability;
40 using FreeCapability = capabilities2_msgs::srv::FreeCapability;
41 using ConnectCapability = capabilities2_msgs::srv::ConnectCapability;
42 using TriggerCapability = capabilities2_msgs::srv::TriggerCapability;
43
44 using CapabilityEventCode = capabilities2_msgs::msg::CapabilityEventCode;
45
46 using GetInterfacesClient = rclcpp::Client<GetInterfaces>;
47 using GetSemanticInterfacesClient = rclcpp::Client<GetSemanticInterfaces>;
48 using GetProvidersClient = rclcpp::Client<GetProviders>;
49 using EstablishBondClient = rclcpp::Client<EstablishBond>;
50 using UseCapabilityClient = rclcpp::Client<UseCapability>;
51 using FreeCapabilityClient = rclcpp::Client<FreeCapability>;
52 using ConnectCapabilityClient = rclcpp::Client<ConnectCapability>;
53 using TriggerCapabilityClient = rclcpp::Client<TriggerCapability>;
54
56
57 };
58
59 virtual ~CapabilityClient() = default;
60
66 virtual void initialize(const rclcpp::Node::SharedPtr& node)
67 {
68 node_ = node;
69
70 if (!node_->has_parameter("capability_client.service_wait_timeout_sec"))
71 node_->declare_parameter<int>("capability_client.service_wait_timeout_sec", -1);
72 service_wait_timeout_sec_ = node_->get_parameter("capability_client.service_wait_timeout_sec").as_int();
73
74 node_->declare_parameter<std::string>("capability_client.services.get_interfaces", "/capabilities/get_interfaces");
75 node_->declare_parameter<std::string>("capability_client.services.get_semantic_interfaces", "/capabilities/get_semantic_interfaces");
76 node_->declare_parameter<std::string>("capability_client.services.get_providers", "/capabilities/get_providers");
77 node_->declare_parameter<std::string>("capability_client.services.establish_bond", "/capabilities/establish_bond");
78 node_->declare_parameter<std::string>("capability_client.services.use_capability", "/capabilities/use_capability");
79 node_->declare_parameter<std::string>("capability_client.services.free_capability", "/capabilities/free_capability");
80 node_->declare_parameter<std::string>("capability_client.services.trigger_capability", "/capabilities/trigger_capability");
81 node_->declare_parameter<std::string>("capability_client.services.connect_capability", "/capabilities/connect_capability");
82
83 node_->get_parameter("capability_client.services.get_interfaces", get_interfaces_);
84 node_->get_parameter("capability_client.services.get_semantic_interfaces", get_semantic_interfaces_);
85 node_->get_parameter("capability_client.services.get_providers", get_providers_);
86 node_->get_parameter("capability_client.services.establish_bond", establish_bond_);
87 node_->get_parameter("capability_client.services.use_capability", use_capability_);
88 node_->get_parameter("capability_client.services.free_capability", free_capability_);
89 node_->get_parameter("capability_client.services.trigger_capability", trigger_capability_);
90 node_->get_parameter("capability_client.services.connect_capability", connect_capability_);
91
100
101 // Wait for services to become available
110
111 RCLCPP_INFO(node_->get_logger(), "[Capability client] initialized.");
112 }
113
121 void getInterfaces(std::vector<CapabilityInfo>& capabilities)
122 {
123 RCLCPP_INFO(node_->get_logger(), "[Capability client] requesting Interface information");
124
125 auto request_interface = std::make_shared<GetInterfaces::Request>();
126
127 bool completed = false;
128 std::mutex mtx;
129 std::condition_variable cv;
130 std::unique_lock<std::mutex> lock(mtx);
131
132 // request data from the server
133 auto result_interface_future = get_interfaces_client_->async_send_request(
134 request_interface, [this, &completed, &cv, &capabilities](GetInterfacesClient::SharedFuture future) {
135 if (!future.valid())
136 {
137 throw fabric::fabric_exception("Failed to get Interface information from "
138 "server");
139 }
140
141 auto response = future.get();
142
143 for (const auto& interface : response->interfaces)
144 {
145 CapabilityInfo info;
146 info.interface = interface;
147 info.is_semantic = false;
148
149 capabilities.push_back(info);
150 }
151
152 completed = true;
153 cv.notify_all();
154 });
155
156 // wait for the response
157 cv.wait(lock, [&completed]() { return completed; });
158
159 RCLCPP_INFO(node_->get_logger(), "[Capability client] received %d interfaces from server\n", static_cast<int>(capabilities.size()));
160 }
161
168 void getSemanticInterfaces(std::vector<CapabilityInfo>& capabilities)
169 {
170 int interface_count = capabilities.size();
171
172 std::vector<CapabilityInfo> new_capabilities;
173
174 for (auto& capability : capabilities)
175 {
176 RCLCPP_INFO(node_->get_logger(), "[Capability client] checking %s for semantic interfaces", capability.interface.c_str());
177
178 auto request_semantic = std::make_shared<GetSemanticInterfaces::Request>();
179 request_semantic->interface = capability.interface;
180
181 bool completed = false;
182 std::mutex mtx;
183 std::condition_variable cv;
184 std::unique_lock<std::mutex> lock(mtx);
185
186 // request semantic interface from the server
187 auto result_semantic_future = get_sem_interf_client_->async_send_request(
188 request_semantic, [this, &new_capabilities, &completed, &cv](GetSemanticInterfacesClient::SharedFuture future) {
189 if (!future.valid())
190 {
191 throw fabric::fabric_exception("Failed to get Semantic Interface information from server");
192 }
193
194 auto response = future.get();
195
196 // add semantic interfaces to the capability info
197 for (const auto& interface : response->semantic_interfaces)
198 {
199 CapabilityInfo info;
200 info.interface = interface;
201 info.is_semantic = true;
202
203 new_capabilities.push_back(info);
204 RCLCPP_INFO(node_->get_logger(), "[Capability client] Semantic interface: %s added\n", interface.c_str());
205 }
206
207 completed = true;
208 cv.notify_all();
209 });
210
211 // wait for the response
212 cv.wait(lock, [&completed]() { return completed; });
213 }
214
215 // append the new semantic interfaces to the original capabilities list
216 capabilities.insert(capabilities.end(), new_capabilities.begin(), new_capabilities.end());
217 }
218
226 void getProviders(std::vector<CapabilityInfo>& capabilities)
227 {
228 for (auto& capability : capabilities)
229 {
230 RCLCPP_INFO(node_->get_logger(), "[Capability client] requesting provider for %s", capability.interface.c_str());
231
232 auto request_providers = std::make_shared<GetProviders::Request>();
233
234 // request providers of the semantic interface
235 request_providers->interface = capability.interface;
236 request_providers->include_semantic = capability.is_semantic;
237
238 bool completed = false;
239 std::mutex mtx;
240 std::condition_variable cv;
241 std::unique_lock<std::mutex> lock(mtx);
242
243 auto result_providers_future =
244 get_providers_client_->async_send_request(request_providers, [this, &capability, &completed, &cv](GetProvidersClient::SharedFuture future) {
245 if (!future.valid())
246 {
247 throw fabric::fabric_exception("Failed to get Provider information from server");
248 }
249
250 auto response = future.get();
251 capability.alt_providers = response->providers;
252 capability.provider = response->default_provider;
253 completed = true;
254 cv.notify_all();
255 });
256
257 // wait for the response
258 cv.wait(lock, [&completed]() { return completed; });
259
260 RCLCPP_INFO(node_->get_logger(), "[Capability client] received for %s, default provider: %s, number of alternative providers: %d\n",
261 capability.interface.c_str(), capability.provider.c_str(), static_cast<int>(capability.alt_providers.size()));
262 }
263 }
264
270 std::string request_bond()
271 {
272 RCLCPP_INFO(node_->get_logger(), "[Capability client] requesting bond id");
273
274 // create bond establishing server request
275 auto request_bond = std::make_shared<EstablishBond::Request>();
276
277 std::string bond_id;
278 bool completed = false;
279 std::mutex mtx;
280 std::condition_variable cv;
281 std::unique_lock<std::mutex> lock(mtx);
282
283 // send the request
284 auto result_future =
285 establish_bond_client_->async_send_request(request_bond, [&bond_id, &completed, &cv, this](EstablishBondClient::SharedFuture future) {
286 if (!future.valid())
287 {
288 throw fabric::fabric_exception("Failed to establish bond with capabilities2 server");
289 }
290
291 auto response = future.get();
292 bond_id = response->bond_id;
293 RCLCPP_INFO(node_->get_logger(), "[Capability client] received bond id : %s\n", bond_id.c_str());
294
295 completed = true;
296 cv.notify_all();
297 });
298
299 // wait for the response
300 cv.wait(lock, [&completed]() { return completed; });
301
302 return bond_id;
303 }
304
313 {
314 int index = 0;
315
316 for (const auto& [id, connection] : plan.connections)
317 {
318 index++;
319
320 auto request_use = std::make_shared<UseCapability::Request>();
321 request_use->capability = connection.source.interface;
322 request_use->preferred_provider = connection.source.provider;
323 request_use->bond_id = plan.bond_id;
324
325 RCLCPP_INFO(node_->get_logger(), "[Capability client] starting capability %d : %s", index, connection.source.interface.c_str());
326
327 bool completed = false;
328 std::mutex mtx;
329 std::condition_variable cv;
330 std::unique_lock<std::mutex> lock(mtx);
331
332 auto result_future = use_capability_client_->async_send_request(request_use, [this, &completed, &cv](UseCapabilityClient::SharedFuture future) {
333 if (!future.valid())
334 {
335 throw fabric::fabric_exception("Failed to use capability");
336 }
337
338 auto response = future.get();
339 completed = true;
340 cv.notify_all();
341 });
342
343 // wait for the response
344 cv.wait(lock, [&completed]() { return completed; });
345
346 RCLCPP_INFO(node_->get_logger(), "[Capability client] capability %s started successfully.\n", connection.source.interface.c_str());
348 }
349 }
350
359 {
360 // track freed capabilities
361 std::vector<std::string> freed_capabilities;
362
363 // prepare to free all started capabilities
364 for (const auto& capability : started_capabilities_)
365 {
366 // check if the capability is part of the plan connections
367 bool found = false;
368 for (const auto& [id, connection] : plan.connections)
369 {
370 if (connection.source.interface == capability)
371 {
372 found = true;
373 break;
374 }
375 }
376
377 bool completed = false;
378 std::mutex mtx;
379 std::condition_variable cv;
380 std::unique_lock<std::mutex> lock(mtx);
381
382 if (found)
383 {
384 // create free capability request
385 auto request_free = std::make_shared<FreeCapability::Request>();
386 request_free->capability = capability;
387 request_free->bond_id = plan.bond_id;
388
389 // send the request
390 auto result_future = free_capability_client_->async_send_request(
391 request_free, [this, &completed, &cv, &capability](FreeCapabilityClient::SharedFuture future) {
392 if (!future.valid())
393 {
394 throw fabric::fabric_exception("Failed to free capability " + capability);
395 }
396
397 auto response = future.get();
398 RCLCPP_INFO(node_->get_logger(), "[Capability client] capability %s freed successfully.\n", capability.c_str());
399 completed = true;
400 cv.notify_all();
401 });
402
403 // wait for the response
404 cv.wait(lock, [&completed]() { return completed; });
405
406 // track successfully freed capabilities
407 freed_capabilities.push_back(capability);
408 }
409 else
410 {
411 RCLCPP_WARN(node_->get_logger(), "[Capability client] capability %s was not started as part of the plan, skipping free request.",
412 capability.c_str());
413 }
414 }
415
416 // remove freed capabilities from started list
417 for (const auto& capability : freed_capabilities)
418 started_capabilities_.erase(std::remove(started_capabilities_.begin(), started_capabilities_.end(), capability), started_capabilities_.end());
419 }
420
421 void connect_capability(const std::string& bond_id, uint8_t code, const fabric::node& source, const fabric::node& target)
422 {
423 auto request = std::make_shared<ConnectCapability::Request>();
424
425 request->bond_id = bond_id;
426
427 request->connection.type.code = code;
428
429 request->connection.source = source.parameters.toMsg();
430 request->connection.source.capability = source.interface;
431 request->connection.source.provider = source.provider;
432 request->connection.source.instance_id = std::to_string(source.instance_id);
433
434 request->connection.target = target.parameters.toMsg();
435 request->connection.target.capability = target.interface;
436 request->connection.target.provider = target.provider;
437 request->connection.target.instance_id = std::to_string(target.instance_id);
438
439 bool completed = false;
440 std::mutex mtx;
441 std::condition_variable cv;
442 std::unique_lock<std::mutex> lock(mtx);
443
444 // send the request
445 auto result_future =
446 connect_capability_client_->async_send_request(request, [this, &completed, &cv](ConnectCapabilityClient::SharedFuture future) {
447 if (!future.valid())
448 {
449 throw fabric::fabric_exception("Failed to configure capability connection event");
450 }
451
452 auto response = future.get();
453
454 RCLCPP_INFO(node_->get_logger(), "[Capability client] capability connection event configured successfully.\n");
455 completed = true;
456 cv.notify_all();
457 });
458
459 // wait for the response
460 cv.wait(lock, [&completed]() { return completed; });
461 }
462
470 {
471 for (const auto& [id, connection] : plan.connections)
472 {
473 RCLCPP_INFO(node_->get_logger(), "[Capability client] configuring connection for %s/%d", connection.source.interface.c_str(),
475
477 {
478 RCLCPP_INFO(node_->get_logger(), "[Capability client] connection to %s/%d on_start configuration requested",
480 connect_capability(plan.bond_id, CapabilityEventCode::STARTED, connection.source, connection.on_start);
481 }
482
484 {
485 RCLCPP_INFO(node_->get_logger(), "[Capability client] connection to %s/%d on_stop configuration requested",
487 connect_capability(plan.bond_id, CapabilityEventCode::STOPPED, connection.source, connection.on_stop);
488 }
489
491 {
492 RCLCPP_INFO(node_->get_logger(), "[Capability client] connection to %s/%d on_success configuration requested",
494 connect_capability(plan.bond_id, CapabilityEventCode::SUCCEEDED, connection.source, connection.on_success);
495 }
496
498 {
499 RCLCPP_INFO(node_->get_logger(), "[Capability client] connection to %s/%d on_failure configuration requested",
501 connect_capability(plan.bond_id, CapabilityEventCode::FAILED, connection.source, connection.on_failure);
502 }
503 }
504 }
505
513 {
514 auto request_trigger = std::make_shared<TriggerCapability::Request>();
515
516 bool completed = false;
517 std::mutex mtx;
518 std::condition_variable cv;
519 std::unique_lock<std::mutex> lock(mtx);
520
521 request_trigger->bond_id = plan.bond_id;
522 request_trigger->capability.instance_id = std::to_string(plan.connections[0].source.instance_id);
523 request_trigger->capability.capability = plan.connections[0].source.interface;
524 request_trigger->capability.parameters = plan.connections[0].source.parameters.toMsg().parameters;
525
526 // send the request
527 auto result_future =
528 trig_capability_client_->async_send_request(request_trigger, [this, &completed, &cv](TriggerCapabilityClient::SharedFuture future) {
529 if (!future.valid())
530 {
531 throw fabric::fabric_exception("Failed to trigger the first capability.");
532 }
533
534 auto response = future.get();
535 completed = true;
536 cv.notify_all();
537 RCLCPP_INFO(node_->get_logger(), "[Capability client] first capability triggered successfully.");
538 });
539
540 // wait for the response
541 cv.wait(lock, [&completed]() { return completed; });
542 }
543
544protected:
548 template <typename ClientT>
549 void wait_for_service(const std::shared_ptr<ClientT>& client, const std::string& service_name)
550 {
551 using namespace std::chrono_literals;
552
553 auto start = std::chrono::steady_clock::now();
554 while (rclcpp::ok())
555 {
556 if (client->wait_for_service(1s))
557 {
558 RCLCPP_INFO(node_->get_logger(), "[Capability client] %s connected", service_name.c_str());
559 return;
560 }
561
562 RCLCPP_INFO(node_->get_logger(), "[Capability client] %s is not available", service_name.c_str());
563
565 {
566 const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - start);
567 if (elapsed.count() >= service_wait_timeout_sec_)
568 {
569 throw fabric::fabric_exception("Timed out waiting for service: " + service_name);
570 }
571 }
572 }
573
574 throw fabric::fabric_exception("ROS shutdown while waiting for service: " + service_name);
575 }
576
580 rclcpp::Node::SharedPtr node_;
581
583
587 std::string get_interfaces_;
589 std::string get_providers_;
590 std::string establish_bond_;
591 std::string use_capability_;
592 std::string free_capability_;
596
600 std::shared_ptr<bond::Bond> bond_;
601
602 std::vector<std::string> started_capabilities_;
603
605 GetInterfacesClient::SharedPtr get_interfaces_client_;
606
608 GetSemanticInterfacesClient::SharedPtr get_sem_interf_client_;
609
611 GetProvidersClient::SharedPtr get_providers_client_;
612
614 EstablishBondClient::SharedPtr establish_bond_client_;
615
617 UseCapabilityClient::SharedPtr use_capability_client_;
618
620 FreeCapabilityClient::SharedPtr free_capability_client_;
621
623 ConnectCapabilityClient::SharedPtr connect_capability_client_;
624
626 TriggerCapabilityClient::SharedPtr trig_capability_client_;
627};
628} // namespace fabric
Class for capability client interactions.
Definition capability_client.hpp:33
std::string get_semantic_interfaces_
Definition capability_client.hpp:588
void free_capabilities(fabric::Plan &plan)
Request free of all started capabilities from capabilities2 server.
Definition capability_client.hpp:358
rclcpp::Client< GetProviders > GetProvidersClient
Definition capability_client.hpp:48
rclcpp::Client< GetInterfaces > GetInterfacesClient
Definition capability_client.hpp:46
std::string configure_capability_
Definition capability_client.hpp:594
void connect_capabilities(fabric::Plan &plan)
Request connection between capabilities from according to the provided plan.
Definition capability_client.hpp:469
std::string free_capability_
Definition capability_client.hpp:592
void trigger_first_node(fabric::Plan &plan)
Trigger the first node.
Definition capability_client.hpp:512
std::string use_capability_
Definition capability_client.hpp:591
GetProvidersClient::SharedPtr get_providers_client_
Definition capability_client.hpp:611
rclcpp::Client< GetSemanticInterfaces > GetSemanticInterfacesClient
Definition capability_client.hpp:47
capabilities2_msgs::srv::GetSemanticInterfaces GetSemanticInterfaces
Definition capability_client.hpp:36
void getInterfaces(std::vector< CapabilityInfo > &capabilities)
Get the Interfaces from the capabilities2 server via related service client.
Definition capability_client.hpp:121
std::string request_bond()
Request the bond from the capabilities2 server.
Definition capability_client.hpp:270
std::string connect_capability_
Definition capability_client.hpp:595
rclcpp::Client< EstablishBond > EstablishBondClient
Definition capability_client.hpp:49
std::vector< std::string > started_capabilities_
Definition capability_client.hpp:602
rclcpp::Client< ConnectCapability > ConnectCapabilityClient
Definition capability_client.hpp:52
capabilities2_msgs::srv::GetInterfaces GetInterfaces
Definition capability_client.hpp:35
std::shared_ptr< bond::Bond > bond_
Heart beat bond with capabilities server.
Definition capability_client.hpp:600
capabilities2_msgs::srv::FreeCapability FreeCapability
Definition capability_client.hpp:40
rclcpp::Client< TriggerCapability > TriggerCapabilityClient
Definition capability_client.hpp:53
TriggerCapabilityClient::SharedPtr trig_capability_client_
Definition capability_client.hpp:626
virtual void initialize(const rclcpp::Node::SharedPtr &node)
Initialize the capability client with the given ROS2 node.
Definition capability_client.hpp:66
GetInterfacesClient::SharedPtr get_interfaces_client_
Definition capability_client.hpp:605
void use_capabilities(fabric::Plan &plan)
Request use of capability from capabilities2 server.
Definition capability_client.hpp:312
GetSemanticInterfacesClient::SharedPtr get_sem_interf_client_
Definition capability_client.hpp:608
capabilities2_msgs::srv::EstablishBond EstablishBond
Definition capability_client.hpp:38
void getSemanticInterfaces(std::vector< CapabilityInfo > &capabilities)
Get the Semantic Interfaces from the capabilities2 server via related service client.
Definition capability_client.hpp:168
void getProviders(std::vector< CapabilityInfo > &capabilities)
Get the Provider information for the related interfaces. T.
Definition capability_client.hpp:226
ConnectCapabilityClient::SharedPtr connect_capability_client_
Definition capability_client.hpp:623
rclcpp::Client< FreeCapability > FreeCapabilityClient
Definition capability_client.hpp:51
std::string establish_bond_
Definition capability_client.hpp:590
void wait_for_service(const std::shared_ptr< ClientT > &client, const std::string &service_name)
Wait for a service to become available.
Definition capability_client.hpp:549
capabilities2_msgs::srv::GetProviders GetProviders
Definition capability_client.hpp:37
void connect_capability(const std::string &bond_id, uint8_t code, const fabric::node &source, const fabric::node &target)
Definition capability_client.hpp:421
virtual ~CapabilityClient()=default
UseCapabilityClient::SharedPtr use_capability_client_
Definition capability_client.hpp:617
std::string get_interfaces_
service names
Definition capability_client.hpp:587
FreeCapabilityClient::SharedPtr free_capability_client_
Definition capability_client.hpp:620
rclcpp::Client< UseCapability > UseCapabilityClient
Definition capability_client.hpp:50
capabilities2_msgs::msg::CapabilityEventCode CapabilityEventCode
Definition capability_client.hpp:44
std::string get_providers_
Definition capability_client.hpp:589
capabilities2_msgs::srv::ConnectCapability ConnectCapability
Definition capability_client.hpp:41
int service_wait_timeout_sec_
Definition capability_client.hpp:582
capabilities2_msgs::srv::TriggerCapability TriggerCapability
Definition capability_client.hpp:42
CapabilityClient()
Definition capability_client.hpp:55
EstablishBondClient::SharedPtr establish_bond_client_
Definition capability_client.hpp:614
std::string trigger_capability_
Definition capability_client.hpp:593
rclcpp::Node::SharedPtr node_
pointer to the ROS2 node
Definition capability_client.hpp:580
capabilities2_msgs::srv::UseCapability UseCapability
Definition capability_client.hpp:39
Definition parser_base.hpp:9
Definition structs.hpp:80
std::string interface
Definition structs.hpp:81
bool is_semantic
Definition structs.hpp:83
Definition structs.hpp:70
std::map< int, connection > connections
Definition structs.hpp:74
std::string bond_id
Definition structs.hpp:73
Structure representing connections between nodes.
Definition structs.hpp:42
node on_success
Definition structs.hpp:46
node on_start
Definition structs.hpp:44
node source
Definition structs.hpp:43
node on_failure
Definition structs.hpp:47
node on_stop
Definition structs.hpp:45
Base class for driver exceptions.
Definition exception.hpp:14
Structure representing a node in the fabric.
Definition structs.hpp:26
std::string provider
Definition structs.hpp:28
std::string interface
Definition structs.hpp:27
bool exists() const
Definition structs.hpp:32
capabilities2_events::EventParameters parameters
Definition structs.hpp:29
int instance_id
Definition structs.hpp:30