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.
lightbar_manager_node.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2023 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 */
16
17
19#include <algorithm>
20
21namespace lightbar_manager
22{
23
24namespace std_ph = std::placeholders;
25
26LightBarManager::LightBarManager(const rclcpp::NodeOptions &options) : carma_ros2_utils::CarmaLifecycleNode(options)
27{
28 // Create initial config
29 config_ = Config();
30 config_.spin_rate_hz = declare_parameter<double>("spin_rate_hz", config_.spin_rate_hz);
31 config_.normal_operation = declare_parameter<bool>("normal_operation", config_.normal_operation);
32 declare_parameter("lightbar_cda_table");
33 declare_parameter("lightbar_ind_table");
34 declare_parameter("lightbar_priorities");
35 lbm_ = std::make_shared<LightBarManagerWorker>();
36}
37
38carma_ros2_utils::CallbackReturn LightBarManager::handle_on_configure(const rclcpp_lifecycle::State &)
39{
40 // Reset config
41 config_ = Config();
42
43 RCLCPP_INFO_STREAM(rclcpp::get_logger("lightbar_manager"),"Initalizing lightbar manager node...");
44
45 // Load the spin rate param to determine how fast to process messages
46 // Default rate 10.0 Hz
47 get_parameter<double>("spin_rate_hz", config_.spin_rate_hz);
48
49 request_control_server_= create_service<carma_msgs::srv::RequestIndicatorControl>("request_control", std::bind(&LightBarManager::requestControlCallBack, this, std_ph::_1, std_ph::_2, std_ph::_3));
50 release_control_server_= create_service<carma_msgs::srv::ReleaseIndicatorControl>("release_control", std::bind(&LightBarManager::releaseControlCallBack, this, std_ph::_1, std_ph::_2, std_ph::_3));
51 set_indicator_server_= create_service<carma_driver_msgs::srv::SetLightBarIndicator>("set_indicator", std::bind(&LightBarManager::setIndicatorCallBack, this, std_ph::_1, std_ph::_2, std_ph::_3));
52 indicator_control_publisher_ = create_publisher<carma_msgs::msg::LightBarIndicatorControllers>("indicator_control", 5);
53 guidance_state_subscriber_ = create_subscription<carma_planning_msgs::msg::GuidanceState>("state", 5, std::bind(&LightBarManager::stateChangeCallBack, this, std_ph::_1));
54 turn_signal_subscriber_ = create_subscription<automotive_platform_msgs::msg::TurnSignalCommand>("turn_signal_command", 5, std::bind(&LightBarManager::turnSignalCallback, this, std_ph::_1));
55 lightbar_driver_client_ = create_client<carma_driver_msgs::srv::SetLights>("set_lights");
56
57 // Load Conversion table, CDAType to Indicator mapping
58
59 rclcpp::Parameter lightbar_cda_table_param = get_parameter("lightbar_cda_table");
60 if (lightbar_cda_table_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
61 config_.lightbar_cda_table = lightbar_cda_table_param.as_string_array();
62
63 rclcpp::Parameter lightbar_ind_table_param = get_parameter("lightbar_ind_table");
64 if (lightbar_ind_table_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
65 config_.lightbar_ind_table = lightbar_ind_table_param.as_string_array();
66
68 {
69 throw std::invalid_argument("Size of lightbar_cda_table is not same as that of lightbar_ind_table");
70 }
71
73
74 // Initialize indicator control map. Fills with supporting indicators with empty string name as owners.
75 lbm_->setIndicatorControllers();
76
77 // Initialize indicator representation of lightbar status to all OFF
78 for (int i =0; i < INDICATOR_COUNT; i++)
79 lbm_->light_status.push_back(OFF);
80
81 rclcpp::Parameter lightbar_priorities_param = get_parameter("lightbar_priorities");
82 if (lightbar_priorities_param.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET)
83 {
84 config_.lightbar_priorities = lightbar_priorities_param.as_string_array();
85 lbm_->control_priorities = config_.lightbar_priorities;
86 }
87
88 // Take control of green light
89 get_parameter<bool>("normal_operation", config_.normal_operation);
90
91 RCLCPP_INFO_STREAM(rclcpp::get_logger("lightbar_manager"), "Loaded params: " << config_);
92
93 std::vector<LightBarIndicator> denied_list, greens = {GREEN_SOLID, GREEN_FLASH};
94
95 denied_list = lbm_->requestControl(greens, node_name_);
96 if (denied_list.size() != 0)
97 {
99 {
100 RCLCPP_WARN_STREAM(rclcpp::get_logger("lightbar_manager"),"In fuction " << __FUNCTION__ << ", LightBarManager was not able to take control of all green indicators."
101 << ".\n Please check priority list rosparameter, and ensure " << node_name_ << " has the highest priority."
102 << ".\n If this is intended, pass false to normal_operation argument. Exiting...");
104 }
105 else
106 {
107 RCLCPP_WARN_STREAM(rclcpp::get_logger("lightbar_manager"),"In fuction " << __FUNCTION__ << ", LightBarManager was not able to take control of all green indicators."
108 << ".\n Resuming...");
109 }
110 }
111 return CallbackReturn::SUCCESS;
112}
113
114carma_ros2_utils::CallbackReturn LightBarManager::handle_on_activate(const rclcpp_lifecycle::State &)
115{
116 pub_timer_ = create_timer(get_clock(),
117 std::chrono::milliseconds((int)(1 / config_.spin_rate_hz * 1000)),
118 std::bind(&LightBarManager::spinCallBack, this));
119 return CallbackReturn::SUCCESS;
120}
121
123{
124 // All components lose controls and turn the indicators off by giving lightbarmanar the control
125 std::vector<LightBarIndicator> all_indicators;
126
127 RCLCPP_INFO_STREAM(rclcpp::get_logger("lightbar_manager"),"LightBarManager was commanded to turn off all indicators!");
128
129 for (std::pair <LightBarIndicator, std::string> element : lbm_->getIndicatorControllers())
130 all_indicators.push_back(element.first);
131
132 // Reset all controls
133 lbm_->setIndicatorControllers();
134
135 // Give lightbar manager the control which is guaranteed to succeed
136 lbm_->requestControl(all_indicators, node_name_);
137 int response_code = 0;
138 for (auto indicator : all_indicators)
139 {
140 response_code = setIndicator(indicator, OFF, node_name_);
141 if (response_code != 0)
142 RCLCPP_WARN_STREAM(rclcpp::get_logger("lightbar_manager"),"In Function " << __FUNCTION__ << ": LightBarManager was not able to turn off indicator ID:"
143 << indicator << ". Response code: " << response_code);
144 }
145
146 // Once forced them off, release controls in case carma is still running so that lightbar_manager does not hog the control
147 all_indicators.erase(std::remove(all_indicators.begin(), all_indicators.end(), GREEN_FLASH), all_indicators.end());
148 all_indicators.erase(std::remove(all_indicators.begin(), all_indicators.end(), GREEN_SOLID), all_indicators.end());
149 lbm_->releaseControl(all_indicators, node_name_);
150}
151
152bool LightBarManager::requestControlCallBack(const std::shared_ptr<rmw_request_id_t>,
153 const std::shared_ptr<carma_msgs::srv::RequestIndicatorControl::Request> req,
154 std::shared_ptr<carma_msgs::srv::RequestIndicatorControl::Response> resp)
155{
156 std::vector<LightBarIndicator> ind_list, controlled_ind_list;
157 std::vector<LightBarCDAType> controlled_cda_type_list;
158
159 // Use CDAType if the field is not empty in the request
160 if (req->cda_list.size() != 0)
161 {
162 for (auto cda_type : req->cda_list)
163 {
164 // return false if invalid cda_type number
165 if (static_cast<uint8_t>(cda_type.type) >= INDICATOR_COUNT)
166 return false;
167 ind_list.push_back(lbm_->getIndicatorFromCDAType(static_cast<LightBarCDAType>(cda_type.type)));
168 }
169 }
170 else
171 {
172 for (auto indicator : req->ind_list)
173 {
174 // return false if invalid indicator number
175 if (static_cast<uint8_t>(indicator.indicator) >= INDICATOR_COUNT)
176 return false;
177 ind_list.push_back(static_cast<LightBarIndicator>(indicator.indicator));
178 }
179
180 }
181
182 controlled_ind_list = lbm_->requestControl(ind_list, req->requester_name);
183
184 for (auto indicator : controlled_ind_list)
185 {
186 try
187 {
188 // Some indicators do not have mapped CDAType
189 controlled_cda_type_list.push_back(lbm_->getCDATypeFromIndicator(indicator));
190 }
192 {
193 // 4 out of 8 does not have mapping, so skip
194 continue;
195 }
196 }
197
198 // Modify the response
199 resp->cda_list = lbm_->getMsg(controlled_cda_type_list);
200 resp->ind_list = lbm_->getMsg(controlled_ind_list);
201 return true;
202}
203
204bool LightBarManager::releaseControlCallBack(const std::shared_ptr<rmw_request_id_t>,
205 const std::shared_ptr<carma_msgs::srv::ReleaseIndicatorControl::Request> req,
206 std::shared_ptr<carma_msgs::srv::ReleaseIndicatorControl::Response> resp)
207{
208 std::vector<LightBarIndicator> ind_list;
209
210 // Use CDAType if the field is not empty in the request
211 if (req->cda_list.size() != 0)
212 {
213 for (auto cda_type : req->cda_list)
214 {
215 // return false if invalid indicator number
216 if (static_cast<uint8_t>(cda_type.type) >= INDICATOR_COUNT)
217 return false;
218 ind_list.push_back(lbm_->getIndicatorFromCDAType(static_cast<LightBarCDAType>(cda_type.type)));
219 }
220 }
221 else
222 {
223 for (auto indicator : req->ind_list)
224 {
225 // return false if invalid cda_type number
226 if (static_cast<uint8_t>(indicator.indicator) >= INDICATOR_COUNT)
227 return false;
228 ind_list.push_back(static_cast<LightBarIndicator>(indicator.indicator));
229 }
230 }
231 lbm_->releaseControl(ind_list, req->requester_name);
232 return true;
233}
234
236{
237 indicator_control_publisher_->publish(lbm_->getMsg(lbm_->getIndicatorControllers()));
238 return true;
239}
240
241void LightBarManager::stateChangeCallBack(carma_planning_msgs::msg::GuidanceState::UniquePtr msg_ptr)
242{
243 // Relay the msg to state machine
244 LightBarState prev_lightbar_state = lbm_->getCurrentState();
245 lbm_->handleStateChange(*msg_ptr);
246
247 // Change green lights depending on states, no need to check if its current owner, as it will always be for green lights
248 LightBarState curr_lightbar_state = lbm_->getCurrentState();
249 if (curr_lightbar_state != prev_lightbar_state)
250 {
251 switch(curr_lightbar_state)
252 {
253 case DISENGAGED:
254 turnOffAll();
255 break;
256 case ACTIVE:
258 break;
259 case ENGAGED:
261 break;
262 default:
263 break;
264 }
265 }
266}
267
268void LightBarManager::turnSignalCallback(automotive_platform_msgs::msg::TurnSignalCommand::UniquePtr msg_ptr)
269{
270 return processTurnSignal(*msg_ptr);
271}
272
273void LightBarManager::processTurnSignal(const automotive_platform_msgs::msg::TurnSignalCommand& msg)
274{
275 // if not automated
276 if (msg.mode != 1)
277 {
278 return;
279 }
280
281 // check if left or right signal should be controlled, or none at all
282 std::vector<lightbar_manager::LightBarIndicator> changed_turn_signal = lbm_->handleTurnSignal(msg);
283
284
285 if (changed_turn_signal.empty())
286 {
287 return; //no need to do anything if it is same turn signal changed
288 }
289
290 lightbar_manager::IndicatorStatus indicator_status;
291 // check if we should turn off or on given any indicator
292 if (msg.turn_signal == automotive_platform_msgs::msg::TurnSignalCommand::NONE)
293 {
295 }
296 else
297 {
298 indicator_status = lightbar_manager::IndicatorStatus::ON;
299 prev_owners_before_turn_ = lbm_->getIndicatorControllers(); // save the owner if new turn is starting
300
301 }
302 if (lbm_->requestControl(changed_turn_signal, node_name_).empty())
303 {
304 int response_code = 0;
305 response_code = setIndicator(changed_turn_signal[0], indicator_status, node_name_);
306 if (response_code != 0)
307 RCLCPP_ERROR_STREAM(rclcpp::get_logger("lightbar_manager"),"In Function " << __FUNCTION__ << ": LightBarManager was not able to set light of indicator ID:"
308 << changed_turn_signal[0] << ". Response code: " << response_code);
309 }
310 else
311 {
312 std::string turn_string = msg.turn_signal == automotive_platform_msgs::msg::TurnSignalCommand::LEFT ? "left" : "right";
313 RCLCPP_WARN_STREAM(rclcpp::get_logger("lightbar_manager"),"Lightbar was not able to take control of lightbar to indicate " << turn_string << "turn!");
314 return;
315 }
316
317 // release control if it is turning off and put back the previous owners
318 if (indicator_status == lightbar_manager::IndicatorStatus::OFF)
319 {
320 lbm_->releaseControl(changed_turn_signal, node_name_);
321 // we need to force set the <indicator, owner> mapping
322 // as series of requestControl may not be able to exactly achieve previous combination
323 lbm_->setIndicatorControllers(prev_owners_before_turn_);
324 // reset as the turn is finished
326 }
327}
328
329std::shared_ptr<LightBarManagerWorker> LightBarManager::getWorker()
330{
331 return lbm_;
332}
333
334int LightBarManager::setIndicator(LightBarIndicator ind, IndicatorStatus ind_status, const std::string& requester_name)
335{
336 // Handle the msg/service translation
337 int response_code = 0;
338 if (static_cast<uint8_t>(ind) >= INDICATOR_COUNT || static_cast<uint8_t>(ind_status) > 1)
339 {
340 // Invalid indicator ID or ind_status
341 response_code = 1;
342 return response_code;
343 }
344
345 // Check if the requester has control of this light
346 std::string current_controller = lbm_->getIndicatorControllers()[ind];
347 if (requester_name == "" || current_controller != requester_name)
348 {
349 RCLCPP_WARN_STREAM(rclcpp::get_logger("lightbar_manager"),requester_name << " failed to set the LightBarIndicator ID" << ind
350 << " as this was already controlled by " << current_controller);
351 response_code = 1;
352 return response_code;
353 }
354
355 std::vector<IndicatorStatus> light_status_proposed = lbm_->setIndicator(ind, ind_status, requester_name);
356 carma_driver_msgs::msg::LightBarStatus msg = lbm_->getLightBarStatusMsg(light_status_proposed);
357 auto srv = std::make_shared<carma_driver_msgs::srv::SetLights::Request>();
358 srv->set_state = msg;
359
360 auto resp = lightbar_driver_client_->async_send_request(srv);
361
362 auto future_status = resp.wait_for(std::chrono::milliseconds(100));
363 // Try to send the request
364 if (future_status == std::future_status::ready)
365 {
366 // if successful, update the local copy.
367 lbm_->light_status = light_status_proposed;
368 response_code = 0;
369 }
370 else
371 {
372 RCLCPP_WARN_STREAM(rclcpp::get_logger("lightbar_manager"),"In function: " << __FUNCTION__ << ": Failed to set lights. ROSservice call to the driver failed. ");
373 response_code = 2;
374 }
375 return response_code;
376
377}
378
379bool LightBarManager::setIndicatorCallBack(const std::shared_ptr<rmw_request_id_t>,
380 const std::shared_ptr<carma_driver_msgs::srv::SetLightBarIndicator::Request> req,
381 std::shared_ptr<carma_driver_msgs::srv::SetLightBarIndicator::Response> resp)
382{
383 LightBarIndicator indicator;
384 int response_code = 0;
385 if (req->cda_type.type != NULL)
386 indicator = lbm_->getIndicatorFromCDAType(static_cast<LightBarCDAType>(req->cda_type.type));
387 else
388 indicator = static_cast<LightBarIndicator>(req->indicator.indicator);
389
390 response_code = setIndicator(indicator, static_cast<IndicatorStatus>(req->state), req->requester_name);
391 resp->status_code = response_code;
392 if (response_code != 0)
393 return false;
394
395 return true;
396}
397
398} // namespace lightbar_manager
399
400
void turnOffAll()
Miscellaneous function that forces the state to disengaged and turn off all indicators....
carma_ros2_utils::PubPtr< carma_msgs::msg::LightBarIndicatorControllers > indicator_control_publisher_
void turnSignalCallback(automotive_platform_msgs::msg::TurnSignalCommand::UniquePtr msg_ptr)
Callback function for turning signal.
carma_ros2_utils::SubPtr< automotive_platform_msgs::msg::TurnSignalCommand > turn_signal_subscriber_
carma_ros2_utils::ClientPtr< carma_driver_msgs::srv::SetLights > lightbar_driver_client_
carma_ros2_utils::CallbackReturn handle_on_configure(const rclcpp_lifecycle::State &)
bool requestControlCallBack(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< carma_msgs::srv::RequestIndicatorControl::Request > req, std::shared_ptr< carma_msgs::srv::RequestIndicatorControl::Response > resp)
std::shared_ptr< LightBarManagerWorker > getWorker()
Get ptr to lightbar_manager_worker (for ease of unit testing)
carma_ros2_utils::ServicePtr< carma_msgs::srv::ReleaseIndicatorControl > release_control_server_
void processTurnSignal(const automotive_platform_msgs::msg::TurnSignalCommand &msg)
Turn signal callback function helper.
bool releaseControlCallBack(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< carma_msgs::srv::ReleaseIndicatorControl::Request > req, std::shared_ptr< carma_msgs::srv::ReleaseIndicatorControl::Response > resp)
carma_ros2_utils::CallbackReturn handle_on_activate(const rclcpp_lifecycle::State &)
carma_ros2_utils::SubPtr< carma_planning_msgs::msg::GuidanceState > guidance_state_subscriber_
int setIndicator(LightBarIndicator ind, IndicatorStatus ind_status, const std::string &requester_name)
Try to turn the given indicator ON or OFF (comm with hardware) upon the given component's request.
std::map< lightbar_manager::LightBarIndicator, std::string > prev_owners_before_turn_
std::shared_ptr< LightBarManagerWorker > lbm_
void stateChangeCallBack(carma_planning_msgs::msg::GuidanceState::UniquePtr msg_ptr)
LightBarManager(const rclcpp::NodeOptions &)
carma_ros2_utils::ServicePtr< carma_driver_msgs::srv::SetLightBarIndicator > set_indicator_server_
carma_ros2_utils::ServicePtr< carma_msgs::srv::RequestIndicatorControl > request_control_server_
rclcpp::TimerBase::SharedPtr pub_timer_
bool setIndicatorCallBack(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< carma_driver_msgs::srv::SetLightBarIndicator::Request > req, std::shared_ptr< carma_driver_msgs::srv::SetLightBarIndicator::Response > resp)
#define INDICATOR_COUNT
std::vector< std::string > lightbar_priorities
std::vector< std::string > lightbar_cda_table
std::vector< std::string > lightbar_ind_table