Carma-platform v4.2.0
CARMA Platform is built on robot operating system (ROS) and utilizes open source software (OSS) that enables Cooperative Driving Automation (CDA) features to allow Automated Driving Systems to interact and cooperate with infrastructure and other vehicles through communication.
carma_cloud_client_node.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 LEIDOS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
17
18namespace carma_cloud_client
19{
20 namespace std_ph = std::placeholders;
21
22 CarmaCloudClient::CarmaCloudClient(const rclcpp::NodeOptions &options)
23 : carma_ros2_utils::CarmaLifecycleNode(options)
24 {
25 // Create initial config
26 config_ = Config();
27
28 // Declare parameters
29 config_.url = declare_parameter<std::string>("url", config_.url);
30 config_.base_req = declare_parameter<std::string>("base_req", config_.base_req);
31 config_.base_ack = declare_parameter<std::string>("base_ack", config_.base_ack);
32 config_.port = declare_parameter<std::string>("port", config_.port);
33 config_.list = declare_parameter<std::string>("list", config_.list);
34 config_.method = declare_parameter<std::string>("method", config_.method);
35 config_.fetchtime = declare_parameter<int>("fetchtime", config_.fetchtime);
36 config_.webport = declare_parameter<uint16_t>("webport", config_.webport);
37 config_.webip = declare_parameter<std::string>("webip", config_.webip);
38
39 }
40
41 rcl_interfaces::msg::SetParametersResult CarmaCloudClient::parameter_update_callback(const std::vector<rclcpp::Parameter> &parameters)
42 {
43
44 auto error = update_params<std::string>({{"url", config_.url},
45 {"base_req", config_.base_req},
46 {"base_ack", config_.base_ack},
47 {"port", config_.port},
48 {"list", config_.list},
49 {"method", config_.method}} , parameters);
50
51 rcl_interfaces::msg::SetParametersResult result;
52
53 result.successful = !error;
54
55 return result;
56 }
57
58 carma_ros2_utils::CallbackReturn CarmaCloudClient::handle_on_configure(const rclcpp_lifecycle::State &)
59 {
60 RCLCPP_DEBUG_STREAM(this->get_logger(), "CarmaCloudClient trying to configure");
61 // Reset config
62 config_ = Config();
63 // Load parameters
64 get_parameter<std::string>("url", config_.url);
65 get_parameter<std::string>("base_req", config_.base_req);
66 get_parameter<std::string>("base_ack", config_.base_ack);
67 get_parameter<std::string>("port", config_.port);
68 get_parameter<std::string>("list", config_.list);
69 get_parameter<std::string>("method", config_.method);
70 get_parameter<int>("fetchtime", config_.fetchtime);
71 get_parameter<std::string>("webip", config_.webip);
72 get_parameter<uint16_t>("webport", config_.webport);
73
74 // Register runtime parameter update callback
75 add_on_set_parameters_callback(std::bind(&CarmaCloudClient::parameter_update_callback, this, std_ph::_1));
76 // Setup subscribers
77 tcr_sub_ = create_subscription<carma_v2x_msgs::msg::TrafficControlRequest>("outgoing_geofence_request", 10,
78 std::bind(&CarmaCloudClient::tcr_callback, this, std_ph::_1));
79 tcm_pub_ = create_publisher<j2735_v2x_msgs::msg::TrafficControlMessage>("incoming_j2735_geofence_control", 1);
80 std::thread webthread(&CarmaCloudClient::StartWebService,this);
81 webthread.detach(); // wait for the thread to finish
82 // Return success if everything initialized successfully
83 return CallbackReturn::SUCCESS;
84 }
85
86 void CarmaCloudClient::tcr_callback(carma_v2x_msgs::msg::TrafficControlRequest::UniquePtr msg)
87 {
88 carma_v2x_msgs::msg::TrafficControlRequest request_msg(*msg.get());
89
90 char xml_str[10000];
91
92 XMLconversion(xml_str, request_msg);
93
95
96 RCLCPP_DEBUG_STREAM( get_logger(), "tcr_sub_ callback called ");
97 }
98
99 void CarmaCloudClient::XMLconversion(char* xml_str, carma_v2x_msgs::msg::TrafficControlRequest request_msg)
100 {
101 j2735_v2x_msgs::msg::TrafficControlRequest j2735_tcr;
102
103 j2735_convertor::geofence_request::convert(request_msg, j2735_tcr);
104
105 RCLCPP_DEBUG_STREAM( get_logger(), "converted: ");
106
107 size_t hexlen = 2; //size of each hex representation with a leading 0
108 char reqid[j2735_tcr.tcr_v01.reqid.id.size() * hexlen + 1];
109 for (int i = 0; i < j2735_tcr.tcr_v01.reqid.id.size(); i++)
110 {
111 sprintf(reqid+(i*hexlen), "%.2X", j2735_tcr.tcr_v01.reqid.id[i]);
112 }
113
114 RCLCPP_DEBUG_STREAM( get_logger(), "reqid: " << reqid);
115
116 long int reqseq = j2735_tcr.tcr_v01.reqseq;
117 RCLCPP_DEBUG_STREAM( get_logger(), "reqseq: " << reqseq);
118
119 long int scale = j2735_tcr.tcr_v01.scale;
120 RCLCPP_DEBUG_STREAM( get_logger(), "scale: " << scale);
121
122 int totBounds = j2735_tcr.tcr_v01.bounds.size();
123 int cnt=0;
124 char bounds_str[5000];
125 strcpy(bounds_str,"");
126
127 // get current time
128 std::time_t tm = this->now().seconds()/60 - config_.fetchtime*24*60; // T minus fetchtime*24 hours in min
129
130 while(cnt<totBounds)
131 {
132
133 uint32_t oldest = tm;
134 long lat = j2735_tcr.tcr_v01.bounds[cnt].reflat;
135 long longg = j2735_tcr.tcr_v01.bounds[cnt].reflon;
136
137
138 long dtx0 = j2735_tcr.tcr_v01.bounds[cnt].offsets[0].deltax;
139 long dty0 = j2735_tcr.tcr_v01.bounds[cnt].offsets[0].deltay;
140 long dtx1 = j2735_tcr.tcr_v01.bounds[cnt].offsets[1].deltax;
141 long dty1 = j2735_tcr.tcr_v01.bounds[cnt].offsets[1].deltay;
142 long dtx2 = j2735_tcr.tcr_v01.bounds[cnt].offsets[2].deltax;
143 long dty2 = j2735_tcr.tcr_v01.bounds[cnt].offsets[2].deltay;
144
145 int x = std::sprintf(bounds_str+strlen(bounds_str),"<bounds><TrafficControlBounds><oldest>%ld</oldest><reflon>%ld</reflon><reflat>%ld</reflat><offsets><OffsetPoint><deltax>%ld</deltax><deltay>%ld</deltay></OffsetPoint><OffsetPoint><deltax>%ld</deltax><deltay>%ld</deltay></OffsetPoint><OffsetPoint><deltax>%ld</deltax><deltay>%ld</deltay></OffsetPoint></offsets></TrafficControlBounds></bounds>",oldest,longg,lat,dtx0,dty0,dtx1,dty1,dtx2,dty2);
146
147 cnt++;
148
149 }
150
151
152
153 char port[config_.port.size() + 1];
154 strcpy(port, config_.port.c_str());
155
156 char list[config_.list.size() + 1];
157 strcpy(list, config_.list.c_str());
158
159 // with port and list
160 sprintf(xml_str,"<?xml version=\"1.0\" encoding=\"UTF-8\"?><TrafficControlRequest port=\"%s\" list=\"%s\"><reqid>%s</reqid><reqseq>%ld</reqseq><scale>%ld</scale>%s</TrafficControlRequest>",port, list, reqid, reqseq,scale,bounds_str);
161
162 RCLCPP_DEBUG_STREAM( get_logger(), "xml_str: " << xml_str);
163
164 }
165
166 int CarmaCloudClient::CloudSend(const std::string &local_msg, const std::string& local_url, const std::string& local_base, const std::string& local_method)
167 {
168 CURL *req;
169 CURLcode res;
170 std::string urlfull = local_url + config_.port + local_base;
171 RCLCPP_DEBUG_STREAM( get_logger(), "full url: " << urlfull);
172 req = curl_easy_init();
173 if(req) {
174 curl_easy_setopt(req, CURLOPT_URL, urlfull.c_str());
175
176 if(strcmp(local_method.c_str(),"POST")==0)
177 {
178 curl_easy_setopt(req, CURLOPT_POSTFIELDS, local_msg.c_str());
179 curl_easy_setopt(req, CURLOPT_TIMEOUT_MS, 1000L); // Request operation complete within max millisecond timeout
180 res = curl_easy_perform(req);
181 if(res != CURLE_OK)
182 {
183 RCLCPP_ERROR_STREAM( get_logger(), "curl send failed: " << curl_easy_strerror(res));
184 return 1;
185 }
186 }
187 curl_easy_cleanup(req);
188 }
189
190 return 0;
191 }
192
193 void CarmaCloudClient::CloudSendAsync(const std::string& local_msg,const std::string& local_url, const std::string& local_base, const std::string& local_method)
194 {
195 std::thread t([this, &local_msg, &local_url, &local_base, &local_method](){
196 CloudSend(local_msg, local_url, local_base, local_method);
197 });
198 t.detach();
199 }
200
201 QByteArray CarmaCloudClient::UncompressBytes(const QByteArray compressedBytes) const
202 {
203 z_stream strm;
204 strm.zalloc = nullptr;//Refer to zlib docs (https://zlib.net/zlib_how.html)
205 strm.zfree = nullptr;
206 strm.opaque = nullptr;
207 strm.avail_in = compressedBytes.size();
208 strm.next_in = (Byte *)compressedBytes.data();
209 //checking input z_stream to see if there is any error, eg: invalid data etc.
210 auto err = inflateInit2(&strm, MAX_WBITS + 16); // gzip input
211 QByteArray outBuf;
212 //MAX numbers of bytes stored in a buffer
213 const int BUFFER_SIZE = 4092;
214 //There is successful, starting to decompress data
215 if (err == Z_OK)
216 {
217 int isDone = 0;
218 do
219 {
220 char buffer[BUFFER_SIZE] = {0};
221 strm.avail_out = BUFFER_SIZE;
222 strm.next_out = (Byte *)buffer;
223 //Uncompress finished
224 isDone = inflate(&strm, Z_FINISH);
225 outBuf.append(buffer);
226 } while (Z_STREAM_END != isDone); //Reach the end of stream to be uncompressed
227 }
228 else
229 {
230 RCLCPP_DEBUG_STREAM( get_logger(), "Error initalize stream. Err code = " << err);
231 }
232 //Finished decompress data stream
233 inflateEnd(&strm);
234 return outBuf;
235 }
236
237 void CarmaCloudClient::TCMHandler(QHttpEngine::Socket *socket)
238 {
239 QString st;
240 while(socket->bytesAvailable()>0)
241 {
242 auto readBytes = socket->readAll();
243 if (socket->headers().keys().contains(CONTENT_ENCODING_KEY) && std::string(socket->headers().constFind(CONTENT_ENCODING_KEY).value().data()) == CONTENT_ENCODING_VALUE)
244 {
245 //readBytes is compressed in gzip format
246 st.append(UncompressBytes(readBytes));
247 }
248 else
249 {
250 st.append(readBytes);
251 }
252
253 }
254 QByteArray array = st.toLocal8Bit();
255
256 char* _cloudUpdate = array.data(); // would be the cloud update packet, needs parsing
257
258
259 std::string tcm_string = _cloudUpdate;
260
261 RCLCPP_DEBUG_STREAM( get_logger(), "Received TCM from cloud");
262 RCLCPP_DEBUG_STREAM( get_logger(), "TCM in XML format: " << tcm_string);
263 if(tcm_string.length() == 0)
264 {
265 RCLCPP_DEBUG_STREAM( get_logger(), "Received TCM length is zero, and skipped.");
266 return;
267 }
268
269 boost::property_tree::ptree list_tree;
270 std :: stringstream ss;
271 ss << tcm_string;
272 read_xml(ss, list_tree);
273
274
275 auto child_tcm_list = list_tree.get_child_optional("TrafficControlMessageList");
276
277 if (!child_tcm_list)
278 {
279 auto child_tree = list_tree.get_child("TrafficControlMessage");
280 j2735_v2x_msgs::msg::TrafficControlMessage parsed_tcm = parseTCMXML(child_tree);
281 tcm_pub_->publish(parsed_tcm);
282 }
283 else
284 {
285 auto tcm_list = child_tcm_list.get();
286
287 BOOST_FOREACH(auto &node, list_tree.get_child("TrafficControlMessageList"))
288 {
289 j2735_v2x_msgs::msg::TrafficControlMessage parsed_tcm = parseTCMXML(node.second);
290 tcm_pub_->publish(parsed_tcm);
291 }
292
293 }
294
295
296 }
297
298 j2735_v2x_msgs::msg::TrafficControlMessage CarmaCloudClient::parseTCMXML(boost::property_tree::ptree& tree)
299 {
300 j2735_v2x_msgs::msg::TrafficControlMessage tcm;
301
302 auto child_tcmv01 = tree.get_child_optional("tcmV01");
303 if (!child_tcmv01)
304 {
305 tcm.choice = j2735_v2x_msgs::msg::TrafficControlMessage::RESERVED;
306 return tcm;
307 }
308 else tcm.choice = j2735_v2x_msgs::msg::TrafficControlMessage::TCMV01;
309
310 std::string reqid_string = tree.get<std::string>("tcmV01.reqid");
311 j2735_v2x_msgs::msg::Id64b output;
312 for (std::size_t i = 0; i != reqid_string.size() / 2; ++i)
313 {
314 tcm.tcm_v01.reqid.id[i] = 16 * parse_hex(reqid_string[2 * i]) + parse_hex(reqid_string[2 * i + 1]);
315 }
316
317 tcm.tcm_v01.reqseq = tree.get<uint8_t>("tcmV01.reqseq");
318 tcm.tcm_v01.msgtot = tree.get<uint16_t>("tcmV01.msgtot");
319 tcm.tcm_v01.msgnum = tree.get<uint16_t>("tcmV01.msgnum");
320
321 std::string id_string = tree.get<std::string>("tcmV01.id");
322 for (std::size_t i = 0; i != id_string.size() / 2; ++i)
323 {
324 tcm.tcm_v01.id.id[i] = 16 * parse_hex(id_string[2 * i]) + parse_hex(id_string[2 * i + 1]);
325 }
326
327 tcm.tcm_v01.updated = tree.get<uint64_t>("tcmV01.updated");
328
329 auto tree_package = tree.get_child_optional("tcmV01.package");
330 if (!tree_package)
331 {
332 tcm.tcm_v01.package_exists = false;
333 }
334 else
335 {
336 tcm.tcm_v01.package_exists = true;
337 tcm.tcm_v01.package = parse_package(tree_package.get());
338 }
339
340 auto tree_params = tree.get_child_optional("tcmV01.params");
341 if (!tree_params)
342 {
343 tcm.tcm_v01.params_exists = false;
344 }
345 else
346 {
347 tcm.tcm_v01.params_exists = true;
348 tcm.tcm_v01.params = parse_params(tree_params.get());
349 }
350
351 auto tree_geometry = tree.get_child_optional("tcmV01.geometry");
352 if (!tree_geometry)
353 {
354 tcm.tcm_v01.geometry_exists = false;
355 }
356 else
357 {
358 tcm.tcm_v01.geometry_exists = true;
359 tcm.tcm_v01.geometry = parse_geometry(tree_geometry.get());
360 }
361
362
363 return tcm;
364 }
365
366
368 {
369 //Web services
370 char *placeholderX[1]={0};
371 int placeholderC=1;
372 QCoreApplication a(placeholderC,placeholderX);
373
374 QHostAddress address = QHostAddress(QString::fromStdString (config_.webip));
375 quint16 port = static_cast<quint16>(config_.webport);
376
377 QSharedPointer<OpenAPI::OAIApiRequestHandler> handler(new OpenAPI::OAIApiRequestHandler());
378 handler = QSharedPointer<OpenAPI::OAIApiRequestHandler> (new OpenAPI::OAIApiRequestHandler());
379 auto router = QSharedPointer<OpenAPI::OAIApiRouter>::create();
380 router->setUpRoutes();
381
382 QObject::connect(handler.data(), &OpenAPI::OAIApiRequestHandler::requestReceived, [&](QHttpEngine::Socket *socket) {
383 TCMHandler(socket);
384 });
385
386 QObject::connect(handler.data(), &OpenAPI::OAIApiRequestHandler::requestReceived, [&](QHttpEngine::Socket *socket) {
387 router->processRequest(socket);
388 });
389 QHttpEngine::Server server(handler.data());
390
391 if (!server.listen(address, port)) {
392 qCritical("Unable to listen on the specified port.");
393 return 1;
394 }
395
396 RCLCPP_DEBUG_STREAM(this->get_logger(), "CarmaCloudClient :: Started web service");
397 return a.exec();
398
399 }
400
401 j2735_v2x_msgs::msg::TrafficControlPackage CarmaCloudClient::parse_package(boost::property_tree::ptree& tree)
402 {
403 j2735_v2x_msgs::msg::TrafficControlPackage tcm_pkg;
404
405 auto tree_label = tree.get_child_optional("label");
406 if( !tree_label )
407 {
408 tcm_pkg.label_exists = false;
409 }
410 else
411 {
412 tcm_pkg.label_exists = true;
413 tcm_pkg.label = tree.get<std::string>("label");
414 }
415
416 std::string tcids_string = tree.get<std::string>("tcids");
417
418 return tcm_pkg;
419
420 }
421
422 j2735_v2x_msgs::msg::TrafficControlSchedule CarmaCloudClient::parse_schedule(boost::property_tree::ptree& tree)
423 {
424 j2735_v2x_msgs::msg::TrafficControlSchedule tcm_schedule;
425 tcm_schedule.start = tree.get<uint64_t>("schedule.start");
426
427 auto child_end = tree.get_child_optional("schedule.end");
428 if (!child_end)
429 {
430 tcm_schedule.end_exists = false;
431 }
432 else
433 {
434 tcm_schedule.end_exists = true;
435 tcm_schedule.end = tree.get<uint64_t>("schedule.end");
436 }
437
438 auto child_dow = tree.get_child_optional("schedule.dow");
439 if (!child_dow)
440 {
441 tcm_schedule.dow_exists = false;
442 }
443 else
444 {
445 tcm_schedule.dow_exists = true;
446 std::string dow_string = tree.get<std::string>("schedule.dow");
447 for (size_t i=0; i<dow_string.size(); i++)
448 {
449 tcm_schedule.dow.dow[i] = dow_string[i] - '0';
450 }
451 }
452 auto child_between = tree.get_child_optional("schedule.between");
453 if( !child_between )
454 {
455 tcm_schedule.between_exists = false;
456 }
457 else
458 {
459 tcm_schedule.between_exists = true;
460 auto child_tree = child_between.get();
461
462 for (auto& item : tree.get_child("schedule.between"))
463 {
464 j2735_v2x_msgs::msg::DailySchedule daily;
465 for (auto& which : item.second)
466 {
467 if (which.first == "begin")
468 {
469 daily.begin = which.second.get_value<uint16_t>();
470 }
471 else if (which.first == "duration")
472 {
473 daily.duration = which.second.get_value<uint16_t>();
474 }
475 }
476 tcm_schedule.between.push_back(daily);
477 }
478 }
479 auto child_repeat = tree.get_child_optional("schedule.repeat");
480
481 if(!child_repeat)
482 {
483 tcm_schedule.repeat_exists = false;
484 }
485 else
486 {
487 tcm_schedule.repeat_exists = true;
488 tcm_schedule.repeat.offset = child_repeat.get().get<uint16_t>("offset");
489 tcm_schedule.repeat.period = child_repeat.get().get<uint16_t>("period");
490 tcm_schedule.repeat.span = child_repeat.get().get<uint16_t>("span");
491 }
492
493 return tcm_schedule;
494 }
495
496 j2735_v2x_msgs::msg::TrafficControlDetail CarmaCloudClient::parse_detail(boost::property_tree::ptree& tree)
497 {
498 j2735_v2x_msgs::msg::TrafficControlDetail tcm_detail;
499
500 auto detail_tree = tree.get_child("detail");
501
502
503 auto closed_tree = detail_tree.get_child_optional( "closed" );
504 if (closed_tree)
505 {
506 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::CLOSED_CHOICE;
507
508 auto open = closed_tree.get().get_child_optional("open");
509 if (open)
510 {
511 tcm_detail.closed = j2735_v2x_msgs::msg::TrafficControlDetail::OPEN;
512 }
513
514 auto notopen = closed_tree.get().get_child_optional("notopen");
515 if (notopen)
516 {
517 tcm_detail.closed = j2735_v2x_msgs::msg::TrafficControlDetail::CLOSED;
518 }
519
520 auto taperleft = closed_tree.get().get_child_optional("taperleft");
521 if (taperleft)
522 {
523 tcm_detail.closed = j2735_v2x_msgs::msg::TrafficControlDetail::TAPERLEFT;
524 }
525
526 auto taperright = closed_tree.get().get_child_optional("taperright");
527 if (taperright)
528 {
529 tcm_detail.closed = j2735_v2x_msgs::msg::TrafficControlDetail::TAPERRIGHT;
530 }
531
532 auto openleft = closed_tree.get().get_child_optional("openleft");
533 if (openleft)
534 {
535 tcm_detail.closed = j2735_v2x_msgs::msg::TrafficControlDetail::OPENLEFT;
536 }
537
538 auto openright = closed_tree.get().get_child_optional("openright");
539 if (openright)
540 {
541 tcm_detail.closed = j2735_v2x_msgs::msg::TrafficControlDetail::OPENRIGHT;
542 }
543
544 }
545
546 auto child_dir= detail_tree.get_child_optional( "direction" );
547 if (child_dir)
548 {
549 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::DIRECTION_CHOICE;
550
551 auto direction = child_dir.get().get_child_optional("reverse");
552 if (direction)
553 {
554 tcm_detail.direction = j2735_v2x_msgs::msg::TrafficControlDetail::REVERSE;
555 }
556 else tcm_detail.direction = j2735_v2x_msgs::msg::TrafficControlDetail::FORWARD;
557
558 }
559
560 auto child_chains = detail_tree.get_child_optional( "chains" );
561 if( child_chains )
562 {
563 tcm_detail.chains = j2735_v2x_msgs::msg::TrafficControlDetail::CHAINS_CHOICE;
564 auto no = child_chains.get().get_child_optional("no");
565 if (no)
566 {
567 tcm_detail.chains = j2735_v2x_msgs::msg::TrafficControlDetail::NO;
568 }
569 auto permitted = child_chains.get().get_child_optional("permitted");
570 if (permitted)
571 {
572 tcm_detail.chains = j2735_v2x_msgs::msg::TrafficControlDetail::PERMITTED;
573 }
574 auto required = child_chains.get().get_child_optional("required");
575 if (required)
576 {
577 tcm_detail.chains = j2735_v2x_msgs::msg::TrafficControlDetail::REQUIRED;
578 }
579 }
580
581 auto child_mins = tree.get_child_optional( "detail.minspeed" );
582 if( child_mins )
583 {
584 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MINSPEED_CHOICE;
585 tcm_detail.minspeed = tree.get<float>("detail.minspeed");
586 }
587
588 auto child_maxs = tree.get_child_optional("detail.maxspeed");
589 if( child_maxs )
590 {
591 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXSPEED_CHOICE;
592 tcm_detail.maxspeed = tree.get<uint16_t>("detail.maxspeed");
593 }
594
595 auto child_minhdwy = tree.get_child_optional("detail.minhdwy");
596 if( child_minhdwy )
597 {
598 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MINHDWY_CHOICE;
599 tcm_detail.minhdwy = tree.get<uint16_t>("detail.minhdwy");
600 }
601
602 auto child_maxvehmass = tree.get_child_optional("detail.maxvehmass");
603 if( child_maxvehmass )
604 {
605 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXVEHMASS_CHOICE;
606 tcm_detail.maxvehmass = tree.get<uint16_t>("detail.maxvehmass");
607 }
608
609 auto child_maxvehheight = tree.get_child_optional("detail.maxvehheight");
610 if( child_maxvehheight )
611 {
612 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXVEHHEIGHT_CHOICE;
613 tcm_detail.maxvehheight = tree.get<uint8_t>("detail.maxvehheight");
614 }
615
616 auto child_maxvehwidth = tree.get_child_optional("detail.maxvehwidth");
617 if( child_maxvehwidth )
618 {
619 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXVEHWIDTH_CHOICE;
620 tcm_detail.maxvehwidth = tree.get<uint8_t>("detail.maxvehwidth");
621 }
622
623 auto child_maxvehlength = tree.get_child_optional("detail.maxvehlength");
624 if( child_maxvehlength )
625 {
626 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXVEHLENGTH_CHOICE;
627 tcm_detail.maxvehlength = tree.get<uint16_t>("detail.maxvehlength");
628 }
629
630 auto child_maxvehaxles = tree.get_child_optional("detail.maxvehaxles");
631 if( child_maxvehaxles)
632 {
633 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXVEHAXLES_CHOICE;
634 tcm_detail.maxvehaxles = tree.get<uint8_t>("detail.maxvehaxles");
635 }
636
637 auto child_minvehocc = tree.get_child_optional("detail.minvehocc");
638 if( child_minvehocc)
639 {
640 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MINVEHOCC_CHOICE;
641 tcm_detail.minvehocc = tree.get<uint8_t>("detail.minvehocc");
642 }
643
644 auto child_maxplatoonsize = tree.get_child_optional("detail.maxplatoonsize");
645 if( child_maxplatoonsize)
646 {
647 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MAXPLATOONSIZE_CHOICE;
648 tcm_detail.maxplatoonsize = tree.get<uint8_t>("detail.maxplatoonsize");
649 }
650
651 auto child_minplatoonhdwy = tree.get_child_optional("detail.minplatoonhdwy");
652 if( child_minplatoonhdwy)
653 {
654 tcm_detail.choice = j2735_v2x_msgs::msg::TrafficControlDetail::MINPLATOONHDWY_CHOICE;
655 tcm_detail.minplatoonhdwy = tree.get<uint16_t>("detail.minplatoonhdwy");
656 }
657
658 return tcm_detail;
659 }
660
661 j2735_v2x_msgs::msg::TrafficControlParams CarmaCloudClient::parse_params(boost::property_tree::ptree& tree)
662 {
663 j2735_v2x_msgs::msg::TrafficControlParams tcm_params;
664 for (auto& item : tree.get_child("vclasses"))
665 {
666 j2735_v2x_msgs::msg::TrafficControlVehClass vclass;
667 if (item.first == "any")
668 {
669 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::ANY;
670 }
671 else if (item.first == "pedestrian")
672 {
673 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::PEDESTRIAN;
674 }
675 else if (item.first == "bicycle")
676 {
677 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::BICYCLE;
678 }
679 else if (tree.count("micromobile") != 0)
680 {
681 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::MICROMOBILE;
682 }
683 else if (item.first == "motorcycle")
684 {
685 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::MOTORCYCLE;
686 }
687 else if (item.first == "passenger-car")
688 {
689 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::PASSENGER_CAR;
690 }
691 else if (item.first == "light-truck-van")
692 {
693 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::LIGHT_TRUCK_VAN;
694 }
695 else if (item.first == "bus")
696 {
697 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::BUS;
698 }
699 else if (item.first == "two-axle-six-tire-single-unit-truck")
700 {
701 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::TWO_AXLE_SIX_TIRE_SINGLE_UNIT_TRUCK;
702 }
703 else if (item.first == "three-axle-single-unit-truck")
704 {
705 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::THREE_AXLE_SINGLE_UNIT_TRUCK;
706 }
707 else if (item.first == "four-or-more-axle-single-unit-truck")
708 {
709 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::FOUR_OR_MORE_AXLE_SINGLE_UNIT_TRUCK;
710 }
711 else if (item.first == "four-or-fewer-axle-single-trailer-truck")
712 {
713 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::FOUR_OR_FEWER_AXLE_SINGLE_TRAILER_TRUCK;
714 }
715 else if (item.first == "five-axle-single-trailer-truck")
716 {
717 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::FIVE_AXLE_SINGLE_TRAILER_TRUCK;
718 }
719 else if (item.first == "six-or-more-axle-single-trailer-truck")
720 {
721 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::SIX_OR_MORE_AXLE_SINGLE_TRAILER_TRUCK;
722 }
723 else if (item.first == "five-or-fewer-axle-multi-trailer-truck")
724 {
725 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::FIVE_OR_FEWER_AXLE_MULTI_TRAILER_TRUCK;
726 }
727 else if (item.first =="six-axle-multi-trailer-truck")
728 {
729 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::SIX_AXLE_MULTI_TRAILER_TRUCK;
730 }
731 else if (item.first =="seven-or-more-axle-multi-trailer-truck")
732 {
733 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::SEVEN_OR_MORE_AXLE_MULTI_TRAILER_TRUCK;
734 }
735 else if (item.first =="rail")
736 {
737 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::RAIL;
738 }
739 else if (item.first == "unclassified")
740 {
741 vclass.vehicle_class = j2735_v2x_msgs::msg::TrafficControlVehClass::UNCLASSIFIED;
742 }
743 tcm_params.vclasses.push_back(vclass);
744 }
745 tcm_params.schedule = parse_schedule(tree);
746
747 std::string bool_str = tree.get_value<std::string>("regulatory");
748 if (bool_str == "false") tcm_params.regulatory = false;
749 else tcm_params.regulatory = true;
750
751 tcm_params.detail = parse_detail(tree);
752
753 return tcm_params;
754
755 }
756
757 j2735_v2x_msgs::msg::TrafficControlGeometry CarmaCloudClient::parse_geometry(boost::property_tree::ptree& tree)
758 {
759 j2735_v2x_msgs::msg::TrafficControlGeometry tcm_geometry;
760 tcm_geometry.proj = tree.get<std::string>("proj");
761 tcm_geometry.datum = tree.get<std::string>("datum");
762
763 tcm_geometry.reftime = tree.get<uint64_t>("reftime");
764
765 tcm_geometry.reflon = tree.get<int32_t>("reflon");
766 tcm_geometry.reflat = tree.get<int32_t>("reflat");
767 tcm_geometry.refelv = tree.get<int32_t>("refelv");
768 tcm_geometry.heading = tree.get<int16_t>("heading");
769
770 for (auto& item : tree.get_child("nodes"))
771 {
772 j2735_v2x_msgs::msg::PathNode pathnode;
773 for (auto& which : item.second)
774 {
775 if (which.first == "x")
776 {
777 std::string x_val = which.second.get_value<std::string>();
778 pathnode.x = std::stoll(x_val);
779 }
780
781 else if (which.first == "y")
782 {
783 std::string y_val = which.second.get_value<std::string>();
784 pathnode.y = std::stoll(y_val);
785 }
786 else if (which.first == "z")
787 {
788 pathnode.z_exists = true;
789 std::string z_val = which.second.get_value<std::string>();
790 pathnode.z = std::stoll(z_val);
791 }
792 else if (which.first == "width")
793 {
794 pathnode.width_exists = true;
795 std::string w_val = which.second.get_value<std::string>();
796 pathnode.width = std::stoll(w_val);
797
798 }
799
800 }
801 tcm_geometry.nodes.push_back(pathnode);
802 }
803
804 return tcm_geometry;
805
806 }
807
808 unsigned char CarmaCloudClient::parse_hex(char c)
809 {
810 if ('0' <= c && c <= '9') return c - '0';
811 if ('A' <= c && c <= 'F') return c - 'A' + 10;
812 if ('a' <= c && c <= 'f') return c - 'a' + 10;
813 std::abort();
814 }
815
816
817
818} // carma_cloud_client
819
820#include "rclcpp_components/register_node_macro.hpp"
821
822// Register the component with class_loader
823RCLCPP_COMPONENTS_REGISTER_NODE(carma_cloud_client::CarmaCloudClient)
TODO for USER: Add class description.
j2735_v2x_msgs::msg::TrafficControlGeometry parse_geometry(boost::property_tree::ptree &tree)
Parse xml geometry subtree of tcm msg.
carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &prev_state)
void tcr_callback(carma_v2x_msgs::msg::TrafficControlRequest::UniquePtr msg)
TCR subscription callback.
j2735_v2x_msgs::msg::TrafficControlParams parse_params(boost::property_tree::ptree &tree)
Parse xml params subtree of tcm msg.
j2735_v2x_msgs::msg::TrafficControlSchedule parse_schedule(boost::property_tree::ptree &tree)
Parse xml schedule subtree of tcm msg.
void CloudSendAsync(const std::string &local_msg, const std::string &local_url, const std::string &local_base, const std::string &local_method)
Send async http request to carma cloud.
carma_ros2_utils::PubPtr< j2735_v2x_msgs::msg::TrafficControlMessage > tcm_pub_
unsigned char parse_hex(char c)
Convert Hex char to unsigned char.
j2735_v2x_msgs::msg::TrafficControlMessage parseTCMXML(boost::property_tree::ptree &tree)
Parse xml package of tcm msg using boost property tree xml parser.
void TCMHandler(QHttpEngine::Socket *socket)
Handles the TCM received from CARMA Cloud.
void XMLconversion(char *xml_str, carma_v2x_msgs::msg::TrafficControlRequest request_msg)
Funtion to Convert the TCR into XML format.
int CloudSend(const std::string &local_msg, const std::string &local_url, const std::string &local_base, const std::string &local_method)
Send http request to carma cloud.
QByteArray UncompressBytes(const QByteArray compressedBytes) const
Uncompress bytes using zlib library.
CarmaCloudClient(const rclcpp::NodeOptions &)
Node constructor.
j2735_v2x_msgs::msg::TrafficControlDetail parse_detail(boost::property_tree::ptree &tree)
Parse xml detail subtree of tcm msg.
j2735_v2x_msgs::msg::TrafficControlPackage parse_package(boost::property_tree::ptree &tree)
Parse xml package subtree of tcm msg.
carma_ros2_utils::SubPtr< carma_v2x_msgs::msg::TrafficControlRequest > tcr_sub_
rcl_interfaces::msg::SetParametersResult parameter_update_callback(const std::vector< rclcpp::Parameter > &parameters)
callback for dynamic parameter updates
void convert(const carma_v2x_msgs::msg::PSM &in_msg, carma_perception_msgs::msg::ExternalObject &out_msg, const std::string &map_frame_id, double pred_period, double pred_step_size, const lanelet::projection::LocalFrameProjector &map_projector, const tf2::Quaternion &ned_in_map_rotation, rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock)
Stuct containing the algorithm configuration values for carma_cloud_client.