00001 #include "ScitosBase.h"
00002
00003
00004
00005
00006
00007
00008
00009 template<>
00010 void ScitosBase::setFeature<bool>(std::string name, bool value) {
00011 robot_->setFeatureBool(name, value);
00012 }
00013 template<>
00014 void ScitosBase::setFeature<int>(std::string name, int value) {
00015 robot_->setFeatureInt(name, value);
00016 }
00017 template<>
00018 void ScitosBase::setFeature<float>(std::string name, float value) {
00019 robot_->setFeatureFloat(name, value);
00020 }
00021 template<>
00022 void ScitosBase::setFeature<std::string>(std::string name, std::string value) {
00023 robot_->setFeatureString(name, value);
00024 }
00025
00026
00027 template<>
00028 bool ScitosBase::getFeature(std::string name) {
00029 bool value;
00030 robot_->getFeatureBool(name, value);
00031 return value;
00032 }
00033 template<>
00034 int ScitosBase::getFeature(std::string name) {
00035 int value;
00036 robot_->getFeatureInt(name, value);
00037 return value;
00038 }
00039 template<>
00040 float ScitosBase::getFeature(std::string name) {
00041 float value;
00042 robot_->getFeatureFloat(name, value);
00043 return value;
00044 }
00045 template<>
00046 std::string ScitosBase::getFeature(std::string name) {
00047 MString value;
00048 robot_->getFeatureString(name, value);
00049 return value;
00050 }
00051
00052
00053
00054 ScitosBase::ScitosBase(const char* config_file, int pArgc, char* pArgv[], ros::NodeHandle& nh) :
00055 node_handle_(nh),
00056 dynamic_reconfigure_server_(dynamic_reconfigure_mutex_),
00057 sonar_is_requested_(false)
00058 {
00059 using namespace MetraLabs::base;
00060 using namespace MetraLabs::robotic::base;
00061 using namespace MetraLabs::robotic::robot;
00062
00063 Error tErr;
00064
00066
00067
00068
00069
00070 app_ = new Application(pArgc, pArgv);
00071 if (app_ == NULL) {
00072 ROS_FATAL("Can't create the application!");
00073 exit(-1);
00074 }
00075
00076
00077 class_factory_ = app_->getClassFactory();
00078 if (class_factory_ == NULL) {
00079 ROS_FATAL("Cannot get the ClassFactory!");
00080 exit(-1);
00081 }
00082
00083
00084 ParameterNode tRobotCfg("RobotCfg");
00085 if ((tErr = tRobotCfg.readFromFile(config_file)) != OK) {
00086 ROS_FATAL("Can't read parameter file. Code: %s", getErrorString(tErr).c_str());
00087 exit(-1);
00088 }
00089
00091
00092
00093 blackboard_ = app_->getBlackboard();
00094 if (blackboard_ == NULL) {
00095 ROS_FATAL("Cannot get the Blackboard!");
00096 exit(-1);
00097 }
00098
00100
00101
00102
00103 robot_ = createInstance<Robot>(class_factory_,
00104 "b07fb034-83c1-446c-b2df-0dd6aa46eef6");
00105 if (robot_ == NULL) {
00106 ROS_FATAL("Failed to create the robot.");
00107 exit(-1);
00108 }
00109
00110
00111 int tries = 3;
00112 while(tries-->0 && (tErr = robot_->preInitializeClient(&tRobotCfg)) != OK) {
00113 ROS_WARN("Failed to pre-initialize the robot. Waiting and retrying..");
00114 sleep(2);
00115 }
00116 if (tErr != OK) {
00117 ROS_FATAL("Failed to pre-initialize the robot. Code: %s", getErrorString(tErr).c_str());
00118 exit(-1);
00119 }
00120
00121
00122 robot_->setPhysicalName("Robot", "MyRobot");
00123 robot_->setPhysicalName("BatteryState", "MyRobot.BatteryState");
00124 robot_->setPhysicalName("Drive.Odometry", "MyRobot.Odometry");
00125 robot_->setPhysicalName("Drive.VelocityCmd", "MyRobot.VelocityCmd");
00126 robot_->setPhysicalName("RangeFinder.Sonar.Data", "MyRobot.Sonar");
00127 robot_->setPhysicalName("Bumper.Bumper.Data", "MyRobot.Bumper");
00128 robot_->setPhysicalName("Bumper.Bumper.ResetCmd", "MyRobot.BumperResetCmd");
00129 if ((tErr = robot_->assignToBlackboard(blackboard_, true)) != OK) {
00130 ROS_FATAL("Failed to assign the robot to the blackboard. Code: %s", getErrorString(tErr).c_str());
00131 exit(-1);
00132 }
00133
00134
00135 if ((tErr = robot_->initializeClient(&tRobotCfg)) != OK) {
00136 ROS_FATAL("Failed to initialize the robot. Code: %s", getErrorString(tErr).c_str());
00137 exit(-1);
00138 }
00139
00141
00142
00143
00144 odometry_data_ = NULL;
00145 tErr = getDataFromBlackboard<BlackboardDataOdometry>(blackboard_,
00146 "MyRobot.Odometry", odometry_data_);
00147 if (tErr != OK) {
00148 ROS_FATAL("Failed to get the odometry data from the blackboard. Code: %s", getErrorString(tErr).c_str());
00149 exit(-1);
00150 }
00151 odometry_data_->addCallback(this);
00152
00153
00154 sonar_data_ = NULL;
00155 tErr = getDataFromBlackboard<BlackboardDataRange>(blackboard_,
00156 "MyRobot.Sonar", sonar_data_);
00157 if (tErr != OK) {
00158 ROS_FATAL("Failed to get the Sonar data from the blackboard. Is it specified in the XML config? Code: %s", getErrorString(tErr).c_str());
00159
00160 } else {
00161 sonar_data_->addCallback(this);
00162 }
00163
00164
00165 battery_state_data_ = NULL;
00166 tErr = getDataFromBlackboard<BlackboardDataBatteryState>(blackboard_,
00167 "MyRobot.BatteryState", battery_state_data_);
00168 if (tErr != OK) {
00169 ROS_FATAL("Failed to get the battery state data from the blackboard. Code: %s", getErrorString(tErr).c_str());
00170 exit(-1);
00171 }
00172
00173
00174
00175 bumper_data_ = NULL;
00176 tErr = getDataFromBlackboard<BlackboardDataBumper>(blackboard_,
00177 "MyRobot.Bumper", bumper_data_);
00178 if (tErr != OK) {
00179 ROS_FATAL("Failed to get the bumper data from the blackboard. Code: %s", getErrorString(tErr).c_str());
00180 exit(-1);
00181 }
00182 bumper_data_->addCallback(this);
00183
00185
00186
00187
00188 bumper_reset_cmd_ = NULL;
00189 tErr = getDataFromBlackboard<BlackboardDataUInt8>(blackboard_,
00190 "MyRobot.BumperResetCmd", bumper_reset_cmd_);
00191 if (tErr != OK) {
00192 ROS_FATAL("Failed to get the bumper reset command from the blackboard. Code: %s", getErrorString(tErr).c_str());
00193 exit(-1);
00194 }
00195
00196
00197 velocity_cmd_ = NULL;
00198 tErr = getDataFromBlackboard<BlackboardDataVelocity>(blackboard_,
00199 "MyRobot.VelocityCmd", velocity_cmd_);
00200 if (tErr != OK) {
00201 ROS_FATAL("Failed to get the velocity command from the blackboard. Code: %s", getErrorString(tErr).c_str());
00202 exit(-1);
00203 }
00204
00205
00207
00208
00209 odom_publisher_ = node_handle_.advertise<nav_msgs::Odometry>("/odom", 20);
00210 sonar_publisher_ = node_handle_.advertise<sensor_msgs::Range>("/sonar", 50);
00211 bumper_publisher_ = node_handle_.advertise<metralabs_msgs::ScitosG5Bumper>("/bumper", 20);
00212
00213 cmd_vel_subscriber_ = node_handle_.subscribe("/cmd_vel", 1, &ScitosBase::driveCommandCallback, this);
00214 bumper_reset_subscriber_ = node_handle_.subscribe("/bumper_reset", 1, &ScitosBase::bumperResetCallback, this);
00215
00216
00217 boost::thread(&ScitosBase::checkSubscribersLoop, this, ros::Rate(1));
00218
00219
00221
00222
00223
00224 if ((tErr = blackboard_->startBlackboard()) != OK) {
00225 ROS_FATAL("Failed to start the blackboard. Code: %s", getErrorString(tErr).c_str());
00226 exit(-1);
00227 }
00228
00230
00231 if ((tErr = robot_->startClient()) != OK) {
00232 ROS_FATAL("Failed to start the robot system. Code: %s", getErrorString(tErr).c_str());
00233 exit(-1);
00234 }
00235
00236
00238
00239 ROS_INFO("Starting diagnostics...");
00240
00241 diagnostics_publisher_ = node_handle_.advertise<diagnostic_msgs::DiagnosticArray>("/diagnostics", 50);
00242 boost::thread(&ScitosBase::diagnosticsPublishingLoop, this, ros::Rate(2));
00243
00244
00246
00247 ROS_INFO("Starting dynamic_reconfigure...");
00248
00249
00250 metralabs_ros::ScitosG5Config config;
00251 getFeatures(config);
00252 ROS_INFO_STREAM("This is what the first read gives... " << config.EBC1_Enable24V);
00253
00254
00255
00256 boost::recursive_mutex::scoped_lock dyn_reconf_lock(dynamic_reconfigure_mutex_);
00257
00258 dynamic_reconfigure_server_.updateConfig(config);
00259 dyn_reconf_lock.unlock();
00260
00261
00262 boost::thread(&ScitosBase::dynamicReconfigureUpdaterLoop, this, boost::ref(dynamic_reconfigure_server_),
00263 boost::ref(dynamic_reconfigure_mutex_), ros::Rate(2));
00264
00265
00266 dynamic_reconfigure::Server<metralabs_ros::ScitosG5Config>::CallbackType dyn_reconf_callback_ptr;
00267 dyn_reconf_callback_ptr = boost::bind(&ScitosBase::dynamicReconfigureCallback, this, _1, _2);
00268 dynamic_reconfigure_server_.setCallback(dyn_reconf_callback_ptr);
00269
00270 }
00271
00272 ScitosBase::~ScitosBase() {
00273 setFeature(FEATURE_SONAR, false);
00274
00275 Error tErr;
00276 if ((tErr = blackboard_->stopBlackboard()) != OK)
00277 ROS_ERROR("Failed to stop the blackboard. Code: %s", getErrorString(tErr).c_str());
00278
00279
00280 if ((tErr = robot_->stopClient()) != OK)
00281 ROS_ERROR("Failed to stop the robot system. Code: %s", getErrorString(tErr).c_str());
00282
00283
00284 if ((tErr = robot_->destroyClient()) != OK)
00285 ROS_ERROR("Failed to destroy the robot system. Code: %s", getErrorString(tErr).c_str());
00286
00287
00288 delete app_;
00289 }
00290
00291
00292 void ScitosBase::dynamicReconfigureCallback(metralabs_ros::ScitosG5Config& config, uint32_t level) {
00293
00294
00295 #define MAKRO_SET_FEATURE(NAME) \
00296 ROS_INFO("Setting feature %s to %s", #NAME, config.NAME?"True":"False"); \
00297 setFeature(#NAME, config.NAME)
00298
00299 MAKRO_SET_FEATURE(EBC0_Enable5V);
00300 MAKRO_SET_FEATURE(EBC0_Enable12V);
00301 MAKRO_SET_FEATURE(EBC0_Enable24V);
00302 MAKRO_SET_FEATURE(EBC1_Enable5V);
00303 MAKRO_SET_FEATURE(EBC1_Enable12V);
00304 MAKRO_SET_FEATURE(EBC1_Enable24V);
00305 MAKRO_SET_FEATURE(FreeRunMode);
00306 MAKRO_SET_FEATURE(SonarsActive);
00307 MAKRO_SET_FEATURE(StatusDisplayKnobLock);
00308 MAKRO_SET_FEATURE(StatusDisplayLED);
00309
00310 ROS_DEBUG("Now reading again: (why is this rubbish?)");
00311 metralabs_ros::ScitosG5Config config_read;
00312 getFeatures(config_read);
00313 }
00314
00315 void ScitosBase::getFeatures(metralabs_ros::ScitosG5Config& config) {
00316
00317
00318 #define MAKRO_GET_FEATURE(NAME) \
00319 ROS_DEBUG("Current hardware feature %s is %s", #NAME, config.NAME?"True":"False"); \
00320 config.NAME = getFeature<typeof(config.NAME)>(std::string(#NAME))
00321
00322 MAKRO_GET_FEATURE(EBC0_Enable5V);
00323 MAKRO_GET_FEATURE(EBC0_Enable12V);
00324 MAKRO_GET_FEATURE(EBC0_Enable24V);
00325 MAKRO_GET_FEATURE(EBC1_Enable5V);
00326 MAKRO_GET_FEATURE(EBC1_Enable12V);
00327 MAKRO_GET_FEATURE(EBC1_Enable24V);
00328 MAKRO_GET_FEATURE(FreeRunMode);
00329 MAKRO_GET_FEATURE(SonarsActive);
00330 MAKRO_GET_FEATURE(StatusDisplayKnobLock);
00331 MAKRO_GET_FEATURE(StatusDisplayLED);
00332 }
00333
00334
00335 void ScitosBase::odometryCallbackHandler() {
00336 MTime time;
00337 Pose pose;
00338 Velocity velocity;
00339 float mileage;
00340
00341 odometry_data_->readLock();
00342 odometry_data_->getData(pose, velocity, mileage);
00343 time = odometry_data_->getTimeStamp();
00344 odometry_data_->readUnlock();
00345
00347 ros::Time odom_time = ros::Time().fromNSec(time.getTimeValue()*1000000);
00348 double x = pose.getX();
00349 double y = pose.getY();
00350 double th = pose.getPhi();
00351 double vx = velocity.getVelocityTranslational();
00352 double vth = velocity.getVelocityRotational();
00353
00354
00355 geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(th);
00356
00358 geometry_msgs::TransformStamped odom_tf;
00359 odom_tf.header.stamp = odom_time;
00360 odom_tf.header.frame_id = "/odom";
00361 odom_tf.child_frame_id = "/base_link";
00362
00363 odom_tf.transform.translation.x = x;
00364 odom_tf.transform.translation.y = y;
00365 odom_tf.transform.rotation = odom_quat;
00366
00367
00368 tf_broadcaster_.sendTransform(odom_tf);
00369
00371 nav_msgs::Odometry odom_msg;
00372 odom_msg.header.stamp = odom_time;
00373 odom_msg.header.frame_id = "/odom";
00374 odom_msg.child_frame_id = "/base_link";
00375
00376
00377 odom_msg.pose.pose.position.x = x;
00378 odom_msg.pose.pose.position.y = y;
00379 odom_msg.pose.pose.orientation = odom_quat;
00380
00381
00382 odom_msg.twist.twist.linear.x = vx;
00383 odom_msg.twist.twist.angular.z = vth;
00384
00385
00386 odom_publisher_.publish(odom_msg);
00387 }
00388
00389 void ScitosBase::sonarCallbackHandler() {
00390 static const RangeData::Config* sonar_config = NULL;
00391
00392 sonar_data_->readLock();
00393 MTime timestamp = odometry_data_->getTimeStamp();
00394 const RangeData::Vector& sonar_data = sonar_data_->getRangeData();
00395 if (sonar_config == NULL)
00396 sonar_config = sonar_data_->getConfig();
00397 sonar_data_->readUnlock();
00398
00399 const std::vector<RangeData::Measurement> measurements = sonar_data;
00400 ros::Time sonar_time = ros::Time().fromNSec(timestamp.getTimeValue()*1000000);
00401
00402
00403 if(!sonar_is_requested_) {
00404 setFeature(FEATURE_SONAR, false);
00405 } else {
00406 if (sonar_config == NULL) {
00407 ROS_WARN_THROTTLE(0.5, "Could not yet read sonar config.");
00408 } else {
00409
00410 const RangeData::ConfigCircularArc* sonar_config_circular =
00411 dynamic_cast<const RangeData::ConfigCircularArc*>(sonar_config);
00412
00414
00415
00416 static std::vector<tf::Transform> sonar_transforms;
00417 static bool sonar_transforms_calculated = false;
00418
00419 if(!sonar_transforms_calculated) {
00420 sonar_transforms_calculated = true;
00421 float angle = sonar_config_circular->first_sensor_angle;
00422 for (unsigned int i = 0; i < sonar_config_circular->sensor_cnt; ++i) {
00423 float x = sonar_config_circular->offset_from_center * std::cos(angle) -0.075;
00424 float y = sonar_config_circular->offset_from_center * std::sin(angle);
00425 tf::Quaternion quat;
00426 quat.setEuler(0, 0, angle);
00427 tf::Transform* transform = new tf::Transform(quat, tf::Vector3(x, y, 0.25));
00428 sonar_transforms.push_back(*transform);
00429 angle += sonar_config_circular->sensor_angle_dist;
00430 }
00431
00432 std::vector<tf::Transform>::iterator it = sonar_transforms.begin();
00433 for (int i = 0; it != sonar_transforms.end(); ++it) {
00434 char targetframe[20];
00435 sprintf(targetframe, "/sonar/sonar_%02d", i++);
00436 tf_broadcaster_.sendTransform(
00437 tf::StampedTransform(*it, sonar_time, "/base_link", targetframe)
00438 );
00439 }
00440 }
00441
00443
00444 sensor_msgs::Range sonar_msg;
00445 sonar_msg.header.stamp = sonar_time;
00446 sonar_msg.radiation_type = sensor_msgs::Range::ULTRASOUND;
00447 sonar_msg.field_of_view = sonar_config_circular->cone_angle;
00448 sonar_msg.min_range = 0.2;
00449 sonar_msg.max_range = 3;
00450
00451
00452 {
00453 static int next_sonar_to_send = 0;
00454 char next_sonar_frame[20];
00455 sprintf(next_sonar_frame, "/sonar/sonar_%02d", next_sonar_to_send);
00456
00457
00458
00459 switch(measurements.at(next_sonar_to_send).err.std) {
00460 case RangeData::RANGE_OKAY:
00461 case RangeData::RANGE_OBJECT_SOMEWHERE_IN_RANGE:
00462 case RangeData::RANGE_PROBABLY_OKAY:
00463 case RangeData::RANGE_PROBABLY_OBJECT_SOMEWHERE_IN_RANGE:
00464 sonar_msg.range = measurements.at(next_sonar_to_send).range;
00465 break;
00466 case RangeData::RANGE_NO_OBJECT_WITHIN_RANGE:
00467 case RangeData::RANGE_PROBABLY_NO_OBJECT_WITHIN_RANGE:
00468 case RangeData::RANGE_INVALID_MEASUREMENT:
00469 case RangeData::RANGE_ERR_SENSOR_BROKEN:
00470 case RangeData::RANGE_MASKED:
00471 default:
00472 ;
00473 sonar_msg.range = 0;
00474 }
00475 sonar_msg.header.frame_id = next_sonar_frame;
00476 sonar_publisher_.publish(sonar_msg);
00477
00478
00479 tf_broadcaster_.sendTransform( tf::StampedTransform(
00480 sonar_transforms.at(next_sonar_to_send), ros::Time::now(), "/base_link", next_sonar_frame ) );
00481
00482 ++next_sonar_to_send %= measurements.size();
00483 }
00484
00485
00486
00487
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499 }
00500 }
00501 }
00502
00503 void ScitosBase::bumperDataCallbackHandler() {
00504 bumper_data_->readLock();
00505 BumperData::Vector bumper_values = bumper_data_->getBumperData();
00506 MTime timestamp = odometry_data_->getTimeStamp();
00507 bumper_data_->readUnlock();
00508
00509 bool bumper_pressed = false;
00510 bool motor_stop = false;
00511
00512 #define BUMPER_CODE_PUSHED 0x12
00513 #define BUMPER_CODE_LOCKED 0x02
00514
00515 for (BumperData::Vector::const_iterator it = bumper_values.begin(); it != bumper_values.end(); ++it) {
00516 if (*it == BUMPER_CODE_PUSHED) {
00517 bumper_pressed = true;
00518 motor_stop = true;
00519 break;
00520 }
00521 else if (*it == BUMPER_CODE_LOCKED) {
00522 motor_stop = true;
00523 }
00524 ROS_DEBUG("BumperData was: %X bumper_pressed: %d motor_stop: %d", *it, bumper_pressed, motor_stop);
00525 }
00526
00527 metralabs_msgs::ScitosG5Bumper bumper_msg;
00528 bumper_msg.header.stamp = ros::Time().fromNSec(timestamp.getTimeValue()*1000000);
00529 bumper_msg.bumper_pressed = bumper_pressed;
00530 bumper_msg.motor_stop = motor_stop;
00531
00532 bumper_publisher_.publish(bumper_msg);
00533 }
00534
00535 void ScitosBase::diagnosticsPublishingLoop(ros::Rate loop_rate) {
00536
00537
00538 while (node_handle_.ok()) {
00539 battery_state_data_->readLock();
00540 MString name = static_cast<BatteryState*>(battery_state_data_)->getName();
00541 float voltage = battery_state_data_->getVoltage();
00542 float current = battery_state_data_->getCurrent();
00543 int16_t charge_state = battery_state_data_->getChargeState();
00544 int16_t remaining_time = battery_state_data_->getRemainingTime();
00545 int16_t charger_status= battery_state_data_->getChargerStatus();
00546 MTime timestamp = battery_state_data_->getTimeStamp();
00547 battery_state_data_->readUnlock();
00548
00549 diagnostic_msgs::DiagnosticStatus battery_status;
00550 battery_status.name = "Scitos G5: Battery";
00551 battery_status.message = "undefined";
00552 battery_status.hardware_id = name;
00553
00554
00555 #define VOLTAGE_ERROR_LEVEL 23 // and below
00556 #define VOLTAGE_WARN_LEVEL 24 // and below
00557 #define VOLTAGE_MID_LEVEL 26 // and below // above means HIGH_LEVEL
00558 #define VOLTAGE_FULL_LEVEL 28.8 // and above
00559 #define CHARGER_PLUGGED 1
00560
00561 if(voltage < VOLTAGE_ERROR_LEVEL && charger_status != CHARGER_PLUGGED)
00562 battery_status.level = diagnostic_msgs::DiagnosticStatus::ERROR;
00563 else if(voltage < VOLTAGE_WARN_LEVEL && charger_status != CHARGER_PLUGGED)
00564 battery_status.level = diagnostic_msgs::DiagnosticStatus::WARN;
00565 else
00566 battery_status.level = diagnostic_msgs::DiagnosticStatus::OK;
00567
00568
00569 battery_status.message = "High";
00570 if(voltage < VOLTAGE_MID_LEVEL)
00571 battery_status.message = "Mid";
00572 if(voltage < VOLTAGE_WARN_LEVEL)
00573 battery_status.message = "Low";
00574 if(voltage < VOLTAGE_ERROR_LEVEL)
00575 battery_status.message = "Depleted";
00576 battery_status.message += charger_status == CHARGER_PLUGGED ? ", charging" : ", discharging";
00577 if(voltage >= VOLTAGE_FULL_LEVEL)
00578 battery_status.message = "Fully charged";
00579
00580
00581 battery_status.values.resize(5);
00582 std::stringstream ss;
00583 battery_status.values[0].key = "Voltage";
00584 ss << voltage << " V";
00585 battery_status.values[0].value = ss.str();
00586
00587 ss.str("");
00588 battery_status.values[1].key = "Current";
00589 ss << current << " A";
00590 battery_status.values[1].value = ss.str();
00591
00592 ss.str("");
00593 battery_status.values[2].key = "ChargeState";
00594 ss << charge_state << " %";
00595 battery_status.values[2].value = ss.str();
00596
00597 ss.str("");
00598 battery_status.values[3].key = "RemainingTime";
00599 ss << remaining_time << " min";
00600 battery_status.values[3].value = ss.str();
00601
00602 battery_status.values[4].key = "ChargerStatus";
00603 battery_status.values[4].value = charger_status == CHARGER_PLUGGED ? "plugged" : "unplugged";
00604
00606 diagnostic_msgs::DiagnosticArray diag_array;
00607 diag_array.header.stamp = ros::Time().fromNSec(timestamp.getTimeValue()*1000000);
00608 diag_array.status.push_back(battery_status);
00609 diagnostics_publisher_.publish(diag_array);
00610
00611
00612 loop_rate.sleep();
00613 }
00614 }
00615
00616 void ScitosBase::checkSubscribersLoop(ros::Rate loop_rate) {
00617
00618 sonar_is_requested_ = false;
00619 setFeature(FEATURE_SONAR, false);
00620 while (node_handle_.ok()) {
00622 bool sonar_had_been_requested = sonar_is_requested_;
00623 sonar_is_requested_ = sonar_publisher_.getNumSubscribers() != 0;
00624
00625 if(sonar_is_requested_ != sonar_had_been_requested) {
00626 setFeature(FEATURE_SONAR, sonar_is_requested_);
00627 ROS_INFO_STREAM("Switching sonar feature to: " << (sonar_is_requested_ ? "enabled" : "disabled"));
00628 }
00629 loop_rate.sleep();
00630 }
00631 }
00632
00633 void ScitosBase::dynamicReconfigureUpdaterLoop(
00634 dynamic_reconfigure::Server<metralabs_ros::ScitosG5Config> &dynamic_reconfigure_server,
00635 boost::recursive_mutex &mutex, ros::Rate loop_rate) {
00636 metralabs_ros::ScitosG5Config config;
00637 while (node_handle_.ok()) {
00638 getFeatures(config);
00639 boost::recursive_mutex::scoped_lock lock(mutex);
00640 dynamic_reconfigure_server.updateConfig(config);
00641 lock.unlock();
00642 loop_rate.sleep();
00643 }
00644 }