Fabric 0.2.0
Loading...
Searching...
No Matches
server.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <map>
4#include <deque>
5#include <string>
6#include <thread>
7
8#include <ament_index_cpp/get_package_share_directory.hpp>
9#include <tinyxml2.h>
10#include <uuid/uuid.h>
11#include <rclcpp/rclcpp.hpp>
12#include <pluginlib/class_loader.hpp>
13
18
21
22#include <fabric_msgs/srv/set_fabric_plan.hpp>
23#include <fabric_msgs/srv/cancel_fabric_plan.hpp>
24#include <fabric_msgs/srv/complete_fabric.hpp>
25#include <fabric_msgs/srv/get_plan_status.hpp>
26#include <fabric_msgs/msg/fabric_status.hpp>
27
28namespace fabric
29{
30
35class Fabric : public rclcpp::Node
36{
37public:
38 using SetFabricPlan = fabric_msgs::srv::SetFabricPlan;
39 using CancelFabricPlan = fabric_msgs::srv::CancelFabricPlan;
40 using CompleteFabric = fabric_msgs::srv::CompleteFabric;
41 using GetPlanStatus = fabric_msgs::srv::GetPlanStatus;
42
48 Fabric(const rclcpp::NodeOptions& options = rclcpp::NodeOptions())
49 : Node("Fabric", options)
50 , validation_loader_("fabric_base", "fabric::ValidationBase")
51 , parsing_loader_("fabric_base", "fabric::ParserBase")
52 {
53 try
54 {
55 // Only call setup if this object is already owned by a shared_ptr
56 if (shared_from_this())
57 {
58 initialize();
59 }
60 }
61 catch (const std::bad_weak_ptr&)
62 {
63 // Not yet safe — probably standalone without make_shared
64 }
65 }
66
72 {
73 /*************************************************************************
74 * Parameters
75 ************************************************************************/
76 const std::string default_plan_file_path = ament_index_cpp::get_package_share_directory("fabric_server") + "/plans/default.xml";
77 this->declare_parameter("plan_file_path", default_plan_file_path);
78 plan_file_path_ = this->get_parameter("plan_file_path").as_string();
79
80 /*************************************************************************
81 * Fabric services
82 ************************************************************************/
83 plan_server_ = this->create_service<SetFabricPlan>("/fabric/set_plan",
84 std::bind(&Fabric::setPlanCallback, this, std::placeholders::_1, std::placeholders::_2));
85
86 cancel_server_ = this->create_service<CancelFabricPlan>(
87 "/fabric/cancel_plan", std::bind(&Fabric::cancelPlanCallback, this, std::placeholders::_1, std::placeholders::_2));
88
89 completion_server_ = this->create_service<CompleteFabric>(
90 "/fabric/set_completion", std::bind(&Fabric::setCompleteCallback, this, std::placeholders::_1, std::placeholders::_2));
91
92 get_plan_status_server_ = this->create_service<GetPlanStatus>(
93 "/fabric/get_plan_status", std::bind(&Fabric::getPlanStatusCallback, this, std::placeholders::_1, std::placeholders::_2));
94
95 /*************************************************************************
96 * Initialize Compatibility Validation Plugin
97 ************************************************************************/
98
99 this->declare_parameter("compatibility_validation_plugin", "fabric::CompatibilityValidation");
100 std::string compatibility_validation_plugin_name = this->get_parameter("compatibility_validation_plugin").as_string();
101
102 RCLCPP_INFO(this->get_logger(), "[server] Loading compatibility validation plugin: %s", compatibility_validation_plugin_name.c_str());
103
104 compatibility_validation_plugin_ = validation_loader_.createSharedInstance(compatibility_validation_plugin_name);
105 compatibility_validation_plugin_->initialize(shared_from_this());
106
107 RCLCPP_INFO(this->get_logger(), "[server] Initialized compatibility validation plugin: %s", compatibility_validation_plugin_name.c_str());
108
109 /*************************************************************************
110 * Initialize Parsing Plugins
111 ************************************************************************/
112
113 this->declare_parameter("parsing_plugin", "fabric::XMLParser");
114 std::string parsing_plugin_name = this->get_parameter("parsing_plugin").as_string();
115
116 RCLCPP_INFO(this->get_logger(), "[server] Loading parsing plugin: %s", parsing_plugin_name.c_str());
117
118 parsing_plugin_ = parsing_loader_.createSharedInstance(parsing_plugin_name);
119 parsing_plugin_->initialize(shared_from_this());
120
121 RCLCPP_INFO(this->get_logger(), "[server] Initialized parsing plugin: %s", parsing_plugin_name.c_str());
122
123 /*************************************************************************
124 * Initialize internal components
125 ************************************************************************/
126 capability_client_ = std::make_shared<CapabilityClient>();
127 capability_client_->initialize(this->shared_from_this());
128
129 /*************************************************************************
130 * Load Starter plan from file
131 ************************************************************************/
132 fabric::Plan starter_plan;
133 if (!parsing_plugin_->load_file(plan_file_path_, starter_plan))
134 {
135 RCLCPP_ERROR(this->get_logger(), "[server] Failed to load default plan from file: %s", plan_file_path_.c_str());
136 throw fabric::fabric_exception("Failed to load default plan");
137 }
138 starter_plan.plan_id = generate_uuid();
139 starter_plan.status = PlanStatus::QUEUED;
140
141 plan_queue_.push_back(starter_plan);
142
143 RCLCPP_INFO(this->get_logger(), "[server] Fabric node initialized");
144
145 /*************************************************************************
146 * Initialize process thread
147 ************************************************************************/
148
149 process_thread = std::thread(&Fabric::process, this);
150 }
151
152protected:
157 void process()
158 {
159 while (plan_queue_.size() > 0)
160 {
161 // reset internal data structures
162 reset();
163
164 RCLCPP_INFO(this->get_logger(), "[server] A new Fabric plan processing starting");
165
166 // get the next plan and parse it into a XML document
167 current_plan_ = plan_queue_.front();
168 plan_queue_.pop_front();
169
171 {
172 RCLCPP_INFO(this->get_logger(), "[server] Skipping cancelled plan with id: %s", current_plan_.plan_id.c_str());
173 continue;
174 }
175
176 // parse the plan to extract connections (Fabric::Plan) as per the parsing plugin
177 try
178 {
179 RCLCPP_INFO(this->get_logger(), "[server] Parsing the fabric plan.");
181
183 }
184 catch (const fabric::fabric_exception& e)
185 {
186 RCLCPP_ERROR(this->get_logger(), "[server] Fabric plan parsing failed with error: %s", e.what());
188 continue;
189 }
190 RCLCPP_INFO(this->get_logger(), "[server] Fabric plan parsing completed successfully.");
191
192 // get the capabilities required for the plan
193 try
194 {
195 RCLCPP_INFO(this->get_logger(), "[server] Getting capabilities required for the plan.");
197 capability_client_->getInterfaces(capability_list_);
198 capability_client_->getSemanticInterfaces(capability_list_);
200 }
201 catch (const fabric::fabric_exception& e)
202 {
203 RCLCPP_ERROR(this->get_logger(), "[server] Capability information retrieval failed with error: %s", e.what());
205 continue;
206 }
207
208 RCLCPP_INFO(this->get_logger(), "[server] Capability information retrieval completed successfully.");
209
210 // validate the plan for compatibility as per the validation plugin
211 try
212 {
213 RCLCPP_INFO(this->get_logger(), "[server] Validating the fabric plan for compatibility.");
214
216 }
217 catch (const fabric::fabric_exception& e)
218 {
219 RCLCPP_ERROR(this->get_logger(), "[server] Compatibility validation failed with error: %s", e.what());
221 continue;
222 }
223
224 // Request bond from capabilities2 server
225 try
226 {
227 RCLCPP_INFO(this->get_logger(), "[server] Requesting bond from capabilities2 server.");
229
230 current_plan_.bond_id = capability_client_->request_bond();
231 }
232 catch (const fabric::fabric_exception& e)
233 {
234 RCLCPP_ERROR(this->get_logger(), "[server] Capability bond request failed with error: %s", e.what());
236 continue;
237 }
238
239 // Start new bond client for the new bond id
240 RCLCPP_INFO(this->get_logger(), "[server] Establishing bond with id : %s", current_plan_.bond_id.c_str());
241 bond_client_cache_[current_plan_.bond_id] = std::make_unique<BondClient>(shared_from_this(), current_plan_.bond_id);
243
244 // Remove old bonds if any
245 if (bond_client_cache_.size() > 1)
246 for (auto& [old_bond_id, bond_client] : bond_client_cache_)
247 if (old_bond_id != current_plan_.bond_id)
248 {
249 bond_client->stop();
250 RCLCPP_INFO(this->get_logger(), "[server] Stopping and removing old bond with id : %s", old_bond_id.c_str());
251 }
252
253 RCLCPP_INFO(this->get_logger(), "[server] Bond established with id : %s", current_plan_.bond_id.c_str());
254
255 // Request use of capabilities for the plan
256 try
257 {
258 RCLCPP_INFO(this->get_logger(), "[server] Requesting use of capabilities for the plan.");
260
261 capability_client_->use_capabilities(current_plan_);
262 }
263 catch (const fabric::fabric_exception& e)
264 {
265 RCLCPP_ERROR(this->get_logger(), "[server] Capability usage failed with error: %s", e.what());
267
268 capability_client_->free_capabilities(current_plan_);
269 continue;
270 }
271
272 // connect the capabilities as per the plan
273 try
274 {
275 RCLCPP_INFO(this->get_logger(), "[server] Connecting capabilities as per the plan.");
277
278 capability_client_->connect_capabilities(current_plan_);
279 }
280 catch (const fabric::fabric_exception& e)
281 {
282 RCLCPP_ERROR(this->get_logger(), "[server] Capability connection failed with error: %s", e.what());
284
285 capability_client_->free_capabilities(current_plan_);
286 continue;
287 }
288
289 RCLCPP_INFO(this->get_logger(), "[server] Capabilities connected successfully.");
290
291 // trigger the first capability in the plan
292 try
293 {
294 RCLCPP_INFO(this->get_logger(), "[server] Triggering the first capability in the plan.");
296
297 capability_client_->trigger_first_node(current_plan_);
298 }
299 catch (const fabric::fabric_exception& e)
300 {
301 RCLCPP_ERROR(this->get_logger(), "[server] Capability trigger failed with error: %s", e.what());
302 }
303
304 RCLCPP_INFO(this->get_logger(), "[server] First capability triggered successfully.");
305
306 // wait for the plan to complete
307 {
308 std::unique_lock<std::mutex> lock(plan_mutex_);
309 plan_cv_.wait(lock, [this]() { return plan_completed_; });
310 }
311
313 {
314 RCLCPP_INFO(this->get_logger(), "[server] Fabric plan cancelled. Releasing capabilities.");
315 capability_client_->free_capabilities(current_plan_);
316 continue;
317 }
318
320 RCLCPP_INFO(this->get_logger(), "[server] Fabric processing completed. Waiting for next plan.");
321 }
322 }
323
327 void setPlanCallback(const std::shared_ptr<SetFabricPlan::Request> request, std::shared_ptr<SetFabricPlan::Response> response)
328 {
329 RCLCPP_INFO(this->get_logger(), "[server] Received the request with a plan");
330
331 fabric::Plan new_plan;
332 new_plan.plan = request->plan;
333 new_plan.plan_id = generate_uuid();
334 new_plan.status = PlanStatus::QUEUED;
335
336 if (!parsing_plugin_->check_compatibility(new_plan))
337 {
338 RCLCPP_ERROR(this->get_logger(), "[server] Plan received via service request not compatible with the loaded parser.");
339 response->plan_id = "";
340 response->error = "[server] Plan is not compatible with the loaded parser.";
341 return;
342 }
343 RCLCPP_INFO(this->get_logger(), "[server] Plan accepted from service request message");
344
345 plan_queue_.push_back(new_plan);
346 response->plan_id = new_plan.plan_id;
347 }
348
352 void cancelPlanCallback(const std::shared_ptr<CancelFabricPlan::Request> request, std::shared_ptr<CancelFabricPlan::Response> response)
353 {
354 RCLCPP_INFO(this->get_logger(), "[server] Plan cancelling requested");
355 std::string plan_id = request->plan_id;
356 std::string bond_id_to_cancel;
357 bool found = false;
358
359 // search for the bond id associated with the plan id from current plan or plan queue
360 if (current_plan_.plan_id == plan_id)
361 {
363 bond_id_to_cancel = current_plan_.bond_id;
364 plan_completed_ = true;
365 plan_cv_.notify_all();
366 found = true;
367 }
368 else
369 {
370 for (auto& plan : plan_queue_)
371 {
372 if (plan.plan_id == plan_id)
373 {
374 plan.status = PlanStatus::CANCELLED;
375 bond_id_to_cancel = plan.bond_id;
376 found = true;
377 break;
378 }
379 }
380 }
381
382 // if bond id is found, break the bond and cancel the plan
383 if (!bond_id_to_cancel.empty())
384 {
385 RCLCPP_INFO(this->get_logger(), "[server] Cancelling plan with id: %s", plan_id.c_str());
386
387 for (auto& [bond_id, bond_client] : bond_client_cache_)
388 {
389 if (bond_id == bond_id_to_cancel)
390 {
391 bond_client->stop();
392 RCLCPP_INFO(this->get_logger(), "[server] Bond with id : %s stopped", bond_id.c_str());
393 break;
394 }
395 }
396 }
397
398 response->success = found;
399 }
400
404 void setCompleteCallback(const std::shared_ptr<CompleteFabric::Request> request, std::shared_ptr<CompleteFabric::Response> response)
405 {
406 // mark the plan as completed using plan id
407 RCLCPP_INFO(this->get_logger(), "[server] Plan completion received for plan id: %s", request->plan_id.c_str());
408
409 if (current_plan_.plan_id != request->plan_id)
410 {
411 RCLCPP_WARN(this->get_logger(), "[server] Received plan id does not match the current plan id");
412 }
413 else
414 {
415 RCLCPP_INFO(this->get_logger(), "[server] Current plan id matches the received plan id. Proceeding to complete the plan.");
416 plan_completed_ = true;
417 plan_cv_.notify_all();
418 }
419 }
420
424 void getPlanStatusCallback(const std::shared_ptr<GetPlanStatus::Request> request, std::shared_ptr<GetPlanStatus::Response> response)
425 {
426 response->header.stamp = this->now();
427
428 // Search current plan
429 if (current_plan_.plan_id == request->plan_id)
430 {
431 response->status = status_msg(current_plan_.status);
432
434 response->rejected_list = current_plan_.rejected_list;
435
436 return;
437 }
438
439 // Search plan queue
440 for (const auto& plan : plan_queue_)
441 {
442 if (plan.plan_id == request->plan_id)
443 {
444 response->status = status_msg(plan.status);
445
446 if (plan.status == PlanStatus::VALIDATION_FAILED || plan.status == PlanStatus::PARSE_FAILED)
447 response->rejected_list = plan.rejected_list;
448
449 return;
450 }
451 }
452
453 // Not found
454 response->status.code = fabric_msgs::msg::FabricStatus::UNKNOWN;
455 response->rejected_list.clear();
456 }
457
461 fabric_msgs::msg::FabricStatus status_msg(PlanStatus status) const
462 {
463 fabric_msgs::msg::FabricStatus status_msg;
464 switch (status)
465 {
467 status_msg.code = fabric_msgs::msg::FabricStatus::UNKNOWN;
468 break;
470 status_msg.code = fabric_msgs::msg::FabricStatus::QUEUED;
471 break;
473 status_msg.code = fabric_msgs::msg::FabricStatus::PARSING;
474 break;
476 status_msg.code = fabric_msgs::msg::FabricStatus::PARSE_FAILED;
477 break;
479 status_msg.code = fabric_msgs::msg::FabricStatus::VALIDATING;
480 break;
482 status_msg.code = fabric_msgs::msg::FabricStatus::VALIDATION_FAILED;
483 break;
485 status_msg.code = fabric_msgs::msg::FabricStatus::BONDING;
486 break;
488 status_msg.code = fabric_msgs::msg::FabricStatus::BOND_FAILED;
489 break;
491 status_msg.code = fabric_msgs::msg::FabricStatus::CAPABILITY_STARTING;
492 break;
494 status_msg.code = fabric_msgs::msg::FabricStatus::CAPABILITY_START_FAILED;
495 break;
497 status_msg.code = fabric_msgs::msg::FabricStatus::CAPABILITY_CONNECTING;
498 break;
500 status_msg.code = fabric_msgs::msg::FabricStatus::CAPABILITY_CONNECT_FAILED;
501 break;
503 status_msg.code = fabric_msgs::msg::FabricStatus::RUNNING;
504 break;
506 status_msg.code = fabric_msgs::msg::FabricStatus::COMPLETED;
507 break;
509 status_msg.code = fabric_msgs::msg::FabricStatus::CANCELLED;
510 break;
511 default:
512 status_msg.code = fabric_msgs::msg::FabricStatus::UNKNOWN;
513 break;
514 }
515 return status_msg;
516 }
520 static std::string generate_uuid()
521 {
522 uuid_t uuid;
523 uuid_generate_random(uuid);
524 char uuid_str[40];
525 uuid_unparse(uuid, uuid_str);
526 return std::string(uuid_str);
527 }
528
533 void reset()
534 {
536 capability_list_.clear();
537 bond_id_.clear();
538 plan_completed_ = false;
539 }
540
542 std::deque<fabric::Plan> plan_queue_;
543
546
549 std::mutex plan_mutex_;
550 std::condition_variable plan_cv_;
551
553 std::shared_ptr<CapabilityClient> capability_client_;
554
556 std::vector<CapabilityInfo> capability_list_;
557
559 pluginlib::ClassLoader<fabric::ValidationBase> validation_loader_;
560 pluginlib::ClassLoader<fabric::ParserBase> parsing_loader_;
561
563 std::shared_ptr<fabric::ParserBase> parsing_plugin_;
564
566 std::shared_ptr<fabric::ValidationBase> compatibility_validation_plugin_;
567
569 std::string bond_id_;
570
572 std::map<std::string, std::shared_ptr<BondClient>> bond_client_cache_;
573
575 std::string plan_file_path_;
576
578 rclcpp::Service<SetFabricPlan>::SharedPtr plan_server_;
579
581 rclcpp::Service<CancelFabricPlan>::SharedPtr cancel_server_;
582
584 rclcpp::Service<CompleteFabric>::SharedPtr completion_server_;
585
587 rclcpp::Service<GetPlanStatus>::SharedPtr get_plan_status_server_;
588
590 std::thread process_thread;
591
592}; // class Fabric
593
594} // namespace fabric
Class representing the main server of the fabric.
Definition server.hpp:36
std::shared_ptr< fabric::ParserBase > parsing_plugin_
Definition server.hpp:563
std::vector< CapabilityInfo > capability_list_
Definition server.hpp:556
std::shared_ptr< fabric::ValidationBase > compatibility_validation_plugin_
Definition server.hpp:566
fabric_msgs::srv::CompleteFabric CompleteFabric
Definition server.hpp:40
void getPlanStatusCallback(const std::shared_ptr< GetPlanStatus::Request > request, std::shared_ptr< GetPlanStatus::Response > response)
Callback function to get the status of a fabric plan.
Definition server.hpp:424
pluginlib::ClassLoader< fabric::ValidationBase > validation_loader_
Definition server.hpp:559
std::string bond_id_
Definition server.hpp:569
bool plan_completed_
Definition server.hpp:548
Fabric(const rclcpp::NodeOptions &options=rclcpp::NodeOptions())
Construct a new Fabric object.
Definition server.hpp:48
std::map< std::string, std::shared_ptr< BondClient > > bond_client_cache_
Definition server.hpp:572
void process()
The main process thread of the fabric.
Definition server.hpp:157
void cancelPlanCallback(const std::shared_ptr< CancelFabricPlan::Request > request, std::shared_ptr< CancelFabricPlan::Response > response)
Callback function to cancel a fabric plan.
Definition server.hpp:352
static std::string generate_uuid()
Generate a UUID string for plan tracking.
Definition server.hpp:520
fabric::Plan current_plan_
Definition server.hpp:545
void reset()
Reset internal data structures for processing a new plan.
Definition server.hpp:533
fabric_msgs::srv::CancelFabricPlan CancelFabricPlan
Definition server.hpp:39
std::condition_variable plan_cv_
Definition server.hpp:550
std::shared_ptr< CapabilityClient > capability_client_
Definition server.hpp:553
void setPlanCallback(const std::shared_ptr< SetFabricPlan::Request > request, std::shared_ptr< SetFabricPlan::Response > response)
Callback function to set a new fabric plan.
Definition server.hpp:327
fabric_msgs::srv::GetPlanStatus GetPlanStatus
Definition server.hpp:41
rclcpp::Service< CancelFabricPlan >::SharedPtr cancel_server_
Definition server.hpp:581
rclcpp::Service< SetFabricPlan >::SharedPtr plan_server_
Definition server.hpp:578
std::deque< fabric::Plan > plan_queue_
Definition server.hpp:542
rclcpp::Service< CompleteFabric >::SharedPtr completion_server_
Definition server.hpp:584
void setCompleteCallback(const std::shared_ptr< CompleteFabric::Request > request, std::shared_ptr< CompleteFabric::Response > response)
Callback function to set the completion of a fabric plan.
Definition server.hpp:404
fabric_msgs::srv::SetFabricPlan SetFabricPlan
Definition server.hpp:38
fabric_msgs::msg::FabricStatus status_msg(PlanStatus status) const
Convert internal plan status to fabric_msgs/PlanStatus message.
Definition server.hpp:461
rclcpp::Service< GetPlanStatus >::SharedPtr get_plan_status_server_
Definition server.hpp:587
void initialize()
Initialize the Fabric node, action server and event client.
Definition server.hpp:71
std::string plan_file_path_
Definition server.hpp:575
std::thread process_thread
Definition server.hpp:590
std::mutex plan_mutex_
Definition server.hpp:549
pluginlib::ClassLoader< fabric::ParserBase > parsing_loader_
Definition server.hpp:560
Definition parser_base.hpp:9
PlanStatus
Definition structs.hpp:51
Definition structs.hpp:70
std::vector< std::string > rejected_list
Definition structs.hpp:75
PlanStatus status
Definition structs.hpp:76
std::string plan
Definition structs.hpp:72
std::string plan_id
Definition structs.hpp:71
std::string bond_id
Definition structs.hpp:73
Base class for driver exceptions.
Definition exception.hpp:14
virtual const char * what() const noexcept override
Definition exception.hpp:21