这篇博客主要探讨init的实现过程
ros::init(argc, argv, "add_two_ints_client")
实现代码在init.cpp文件,这里直接给出来
#include "ros/init.h"
#include "ros/names.h"
#include "ros/xmlrpc_manager.h"
#include "ros/poll_manager.h"
#include "ros/connection_manager.h"
#include "ros/topic_manager.h"
#include "ros/service_manager.h"
#include "ros/this_node.h"
#include "ros/network.h"
#include "ros/file_log.h"
#include "ros/callback_queue.h"
#include "ros/param.h"
#include "ros/rosout_appender.h"
#include "ros/subscribe_options.h"
#include "ros/transport/transport_tcp.h"
#include "ros/internal_timer_manager.h"
#include "xmlrpcpp/XmlRpcSocket.h"
#include "roscpp/GetLoggers.h"
#include "roscpp/SetLoggerLevel.h"
#include "roscpp/Empty.h"
#include <ros/console.h>
#include <ros/time.h>
#include <rosgraph_msgs/Clock.h>
#include <algorithm>
#include <signal.h>
#include <cstdlib>
namespace ros
{
namespace master
{
void init(const M_string& remappings);
}
namespace this_node
{
void init(const std::string& names, const M_string& remappings, uint32_t options);
}
namespace network
{
void init(const M_string& remappings);
}
namespace param
{
void init(const M_string& remappings);
}
namespace file_log
{
void init(const M_string& remappings);
}
CallbackQueuePtr g_global_queue;
ROSOutAppender* g_rosout_appender;
static CallbackQueuePtr g_internal_callback_queue;
static bool g_initialized = false;
static bool g_started = false;
static bool g_atexit_registered = false;
static boost::mutex g_start_mutex;
static bool g_ok = false;
static uint32_t g_init_options = 0;
static bool g_shutdown_requested = false;
static volatile bool g_shutting_down = false;
static boost::recursive_mutex g_shutting_down_mutex;
static boost::thread g_internal_queue_thread;
bool isInitialized()
{
return g_initialized;
}
bool isShuttingDown()
{
return g_shutting_down;
}
void checkForShutdown()
{
if (g_shutdown_requested)
{
boost::recursive_mutex::scoped_try_lock lock(g_shutting_down_mutex, boost::defer_lock);
while (!lock.try_lock() && !g_shutting_down)
{
ros::WallDuration(0.001).sleep();
}
if (!g_shutting_down)
{
shutdown();
}
g_shutdown_requested = false;
}
}
void requestShutdown()
{
g_shutdown_requested = true;
}
void atexitCallback()
{
if (ok() && !isShuttingDown())
{
ROSCPP_LOG_DEBUG("shutting down due to exit() or end of main() without cleanup of all NodeHandles");
g_started = false;
shutdown();
}
}
void shutdownCallback(XmlRpc::XmlRpcValue& params, XmlRpc::XmlRpcValue& result)
{
int num_params = 0;
if (params.getType() == XmlRpc::XmlRpcValue::TypeArray)
num_params = params.size();
if (num_params > 1)
{
std::string reason = params[1];
ROS_WARN("Shutdown request received.");
ROS_WARN("Reason given for shutdown: [%s]", reason.c_str());
requestShutdown();
}
result = xmlrpc::responseInt(1, "", 0);
}
bool getLoggers(roscpp::GetLoggers::Request&, roscpp::GetLoggers::Response& resp)
{
std::map<std::string, ros::console::levels::Level> loggers;
bool success = ::ros::console::get_loggers(loggers);
if (success)
{
for (std::map<std::string, ros::console::levels::Level>::const_iterator it = loggers.begin(); it != loggers.end(); it++)
{
roscpp::Logger logger;
logger.name = it->first;
ros::console::levels::Level level = it->second;
if (level == ros::console::levels::Debug)
{
logger.level = "debug";
}
else if (level == ros::console::levels::Info)
{
logger.level = "info";
}
else if (level == ros::console::levels::Warn)
{
logger.level = "warn";
}
else if (level == ros::console::levels::Error)
{
logger.level = "error";
}
else if (level == ros::console::levels::Fatal)
{
logger.level = "fatal";
}
resp.loggers.push_back(logger);
}
}
return success;
}
bool setLoggerLevel(roscpp::SetLoggerLevel::Request& req, roscpp::SetLoggerLevel::Response&)
{
std::transform(req.level.begin(), req.level.end(), req.level.begin(), (int(*)(int))std::toupper);
ros::console::levels::Level level;
if (req.level == "DEBUG")
{
level = ros::console::levels::Debug;
}
else if (req.level == "INFO")
{
level = ros::console::levels::Info;
}
else if (req.level == "WARN")
{
level = ros::console::levels::Warn;
}
else if (req.level == "ERROR")
{
level = ros::console::levels::Error;
}
else if (req.level == "FATAL")
{
level = ros::console::levels::Fatal;
}
else
{
return false;
}
bool success = ::ros::console::set_logger_level(req.logger, level);
if (success)
{
console::notifyLoggerLevelsChanged();
}
return success;
}
bool closeAllConnections(roscpp::Empty::Request&, roscpp::Empty::Response&)
{
ROSCPP_LOG_DEBUG("close_all_connections service called, closing connections");
ConnectionManager::instance()->clear(Connection::TransportDisconnect);
return true;
}
void clockCallback(const rosgraph_msgs::Clock::ConstPtr& msg)
{
Time::setNow(msg->clock);
}
CallbackQueuePtr getInternalCallbackQueue()
{
if (!g_internal_callback_queue)
{
g_internal_callback_queue.reset(new CallbackQueue);
}
return g_internal_callback_queue;
}
void basicSigintHandler(int sig)
{
(void)sig;
ros::requestShutdown();
}
void internalCallbackQueueThreadFunc()
{
disableAllSignalsInThisThread();
CallbackQueuePtr queue = getInternalCallbackQueue();
while (!g_shutting_down)
{
queue->callAvailable(WallDuration(0.1));
}
}
bool isStarted()
{
return g_started;
}
void start()
{
boost::mutex::scoped_lock lock(g_start_mutex);
if (g_started)
{
return;
}
g_shutdown_requested = false;
g_shutting_down = false;
g_started = true;
g_ok = true;
bool enable_debug = false;
std::string enable_debug_env;
if ( get_environment_variable(enable_debug_env,"ROSCPP_ENABLE_DEBUG") )
{
try
{
enable_debug = boost::lexical_cast<bool>(enable_debug_env.c_str());
}
catch (boost::bad_lexical_cast&)
{
}
}
#ifdef _MSC_VER
if (env_ipv6)
{
free(env_ipv6);
}
#endif
param::param("/tcp_keepalive", TransportTCP::s_use_keepalive_, TransportTCP::s_use_keepalive_);
PollManager::instance()->addPollThreadListener(checkForShutdown);
XMLRPCManager::instance()->bind("shutdown", shutdownCallback);
initInternalTimerManager();
TopicManager::instance()->start();
ServiceManager::instance()->start();
ConnectionManager::instance()->start();
PollManager::instance()->start();
XMLRPCManager::instance()->start();
if (!(g_init_options & init_options::NoSigintHandler))
{
signal(SIGINT, basicSigintHandler);
}
ros::Time::init();
if (!(g_init_options & init_options::NoRosout))
{
g_rosout_appender = new ROSOutAppender;
ros::console::register_appender(g_rosout_appender);
}
if (g_shutting_down) goto end;
{
ros::AdvertiseServiceOptions ops;
ops.init<roscpp::GetLoggers>(names::resolve("~get_loggers"), getLoggers);
ops.callback_queue = getInternalCallbackQueue().get();
ServiceManager::instance()->advertiseService(ops);
}
if (g_shutting_down) goto end;
{
ros::AdvertiseServiceOptions ops;
ops.init<roscpp::SetLoggerLevel>(names::resolve("~set_logger_level"), setLoggerLevel);
ops.callback_queue = getInternalCallbackQueue().get();
ServiceManager::instance()->advertiseService(ops);
}
if (g_shutting_down) goto end;
if (enable_debug)
{
ros::AdvertiseServiceOptions ops;
ops.init<roscpp::Empty>(names::resolve("~debug/close_all_connections"), closeAllConnections);
ops.callback_queue = getInternalCallbackQueue().get();
ServiceManager::instance()->advertiseService(ops);
}
if (g_shutting_down) goto end;
{
bool use_sim_time = false;
param::param("/use_sim_time", use_sim_time, use_sim_time);
if (use_sim_time)
{
Time::setNow(ros::Time());
}
if (g_shutting_down) goto end;
if (use_sim_time)
{
ros::SubscribeOptions ops;
ops.init<rosgraph_msgs::Clock>(names::resolve("/clock"), 1, clockCallback);
ops.callback_queue = getInternalCallbackQueue().get();
TopicManager::instance()->subscribe(ops);
}
}
if (g_shutting_down) goto end;
g_internal_queue_thread = boost::thread(internalCallbackQueueThreadFunc);
getGlobalCallbackQueue()->enable();
ROSCPP_LOG_DEBUG("Started node [%s], pid [%d], bound on [%s], xmlrpc port [%d], tcpros port [%d], using [%s] time",
this_node::getName().c_str(), getpid(), network::getHost().c_str(),
XMLRPCManager::instance()->getServerPort(), ConnectionManager::instance()->getTCPPort(),
Time::useSystemTime() ? "real" : "sim");
end:
if (g_shutting_down)
{
boost::recursive_mutex::scoped_lock lock(g_shutting_down_mutex);
}
}
void check_ipv6_environment() {
char* env_ipv6 = NULL;
#ifdef _MSC_VER
_dupenv_s(&env_ipv6, NULL, "ROS_IPV6");
#else
env_ipv6 = getenv("ROS_IPV6");
#endif
bool use_ipv6 = (env_ipv6 && strcmp(env_ipv6,"on") == 0);
TransportTCP::s_use_ipv6_ = use_ipv6;
XmlRpc::XmlRpcSocket::s_use_ipv6_ = use_ipv6;
}
void init(const M_string& remappings, const std::string& name, uint32_t options)
{
if (!g_atexit_registered)
{
g_atexit_registered = true;
atexit(atexitCallback);
}
if (!g_global_queue)
{
g_global_queue.reset(new CallbackQueue);
}
if (!g_initialized)
{
g_init_options = options;
g_ok = true;
ROSCONSOLE_AUTOINIT;
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
check_ipv6_environment();
network::init(remappings);
master::init(remappings);
this_node::init(name, remappings, options);
file_log::init(remappings);
param::init(remappings);
g_initialized = true;
}
}
void init(int& argc, char** argv, const std::string& name, uint32_t options)
{
M_string remappings;
int full_argc = argc;
for (int i = 0; i < argc; )
{
std::string arg = argv[i];
size_t pos = arg.find(":=");
if (pos != std::string::npos)
{
std::string local_name = arg.substr(0, pos);
std::string external_name = arg.substr(pos + 2);
ROSCPP_LOG_DEBUG("remap: %s => %s", local_name.c_str(), external_name.c_str());
remappings[local_name] = external_name;
char *tmp = argv[i];
for (int j = i; j < full_argc - 1; j++)
argv[j] = argv[j+1];
argv[argc-1] = tmp;
argc--;
}
else
{
i++;
}
}
init(remappings, name, options);
}
void init(const VP_string& remappings, const std::string& name, uint32_t options)
{
M_string remappings_map;
VP_string::const_iterator it = remappings.begin();
VP_string::const_iterator end = remappings.end();
for (; it != end; ++it)
{
remappings_map[it->first] = it->second;
}
init(remappings_map, name, options);
}
std::string getROSArg(int argc, const char* const* argv, const std::string& arg)
{
for (int i = 0; i < argc; ++i)
{
std::string str_arg = argv[i];
size_t pos = str_arg.find(":=");
if (str_arg.substr(0,pos) == arg)
{
return str_arg.substr(pos+2);
}
}
return "";
}
void removeROSArgs(int argc, const char* const* argv, V_string& args_out)
{
for (int i = 0; i < argc; ++i)
{
std::string arg = argv[i];
size_t pos = arg.find(":=");
if (pos == std::string::npos)
{
args_out.push_back(arg);
}
}
}
void spin()
{
SingleThreadedSpinner s;
spin(s);
}
void spin(Spinner& s)
{
s.spin();
}
void spinOnce()
{
g_global_queue->callAvailable(ros::WallDuration());
}
void waitForShutdown()
{
while (ok())
{
WallDuration(0.05).sleep();
}
}
CallbackQueue* getGlobalCallbackQueue()
{
return g_global_queue.get();
}
bool ok()
{
return g_ok;
}
void shutdown()
{
boost::recursive_mutex::scoped_lock lock(g_shutting_down_mutex);
if (g_shutting_down)
return;
else
g_shutting_down = true;
ros::console::shutdown();
g_global_queue->disable();
g_global_queue->clear();
if (g_internal_queue_thread.get_id() != boost::this_thread::get_id())
{
g_internal_queue_thread.join();
}
delete g_rosout_appender;
g_rosout_appender = 0;
if (g_started)
{
TopicManager::instance()->shutdown();
ServiceManager::instance()->shutdown();
PollManager::instance()->shutdown();
ConnectionManager::instance()->shutdown();
XMLRPCManager::instance()->shutdown();
}
g_started = false;
g_ok = false;
Time::shutdown();
}
}
需要探讨的函数init是:
void init(int& argc, char** argv, const std::string& name, uint32_t options)
{
M_string remappings;
int full_argc = argc;
for (int i = 0; i < argc; )
{
std::string arg = argv[i];
size_t pos = arg.find(":=");
if (pos != std::string::npos)
{
std::string local_name = arg.substr(0, pos);
std::string external_name = arg.substr(pos + 2);
ROSCPP_LOG_DEBUG("remap: %s => %s", local_name.c_str(), external_name.c_str());
remappings[local_name] = external_name;
char *tmp = argv[i];
for (int j = i; j < full_argc - 1; j++)
argv[j] = argv[j+1];
argv[argc-1] = tmp;
argc--;
}
else
{
i++;
}
}
init(remappings, name, options);
}
- 这个函数有四个输入参数,返回值为空。
参数1:int& argc: 引用类型,由类型的实际值引用(类似于指针)表示的数据类型。如果为某个变量分配一个引用类型,则该变量将引用(或“指向”)原始值。不创建任何副本。引用类型包括类、接口、委托和装箱值类型。这个参数用来表示输入字符串的个数,字符串是在第二个参数中给出。
参数2:char** argv:指向指针的指针类型。定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。(详细见博客)一个字符类型的指针是能够用来表示一个字符串的,所以利用字符类型的指向指针的指针是能够用来表示多个字符串。
参数3:const std::string& name: 节点的名字。const:const是一个C语言(ANSI C)的关键字,具有着举足轻重的地位。它限定一个变量不允许被改变,产生静态作用。使用const在一定程度上可以提高程序的安全性和可靠性。这里的节点名字在函数的实现中是不能改变的,因为这里传入的参数是引用类型,一旦修改后,其他文件中的相应值也被修改,这是不可取的。那这里为什么不是传递单纯的值类型呢?
参数4:uint32_t options: 枚举类型的节点创建的类型,其值定义如下:
NoSigintHandler:通过ctrl-c或者ctrl-z关闭节点失效
AnonymousName:在节点名字后面添加随机数保证节点名字的唯一性(节点的名称有大作用吗?)
NoRosout:不广播节点的一些运行消息到/rosout话题
enum InitOption
{
NoSigintHandler = 1 << 0,
AnonymousName = 1 << 1,
NoRosout = 1 << 2,
};
M_string remappings;
其中M_string
的定义是:
typedef std::map<std::string, std::string> M_string;
map是C++ STL 的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。也就是通过第一个关键字,我们就能找到第二个关键字的数据。举个例子,在使用launch文件启动节点是需要指定一些参数,这时所定义的参数名(name)和参数值(value)就是存在这样的一个容器中,之后在代码中通过参数名去获取参数值。
<arg name="fcu_url" value="$(arg fcu_url)" />
- 提取从字符串数组(字符类型的指向指针的指针)中提取参数。find、substr时string的常见使用类型。
#find()返回值是字母在母串中的位置(下标记录),如果没有找到,那么会返回一个特别的标记npos。(见博客)
substr()是C++语言函数,主要功能是复制子字符串,要求从指定位置开始,并具有指定的长度。如果没有指定长度_Count或_Count+_Off超出了源字符串的长度,则子字符串将延续到源字符串的结尾。
函数原型:
substr(size_type _Off = 0,size_type _Count = npos)
参数:
_Off——所需的子字符串的起始位置。字符串中第一个字符的索引为 0,默认值为0。
_Count——复制的字符数目
返回值——一个子字符串,从其指定的位置开始
具体见博客
ROSCPP_LOG_DEBUG是一个宏定义在文件file_log.h中,打印信息到终端中。
#define ROSCPP_LOG_DEBUG(...) ROS_DEBUG_NAMED("roscpp_internal", __VA_ARGS__)
ROSCPP_LOG_DEBUG
remappings[local_name] = external_name;
调用另一个初始化函数init
void init(const M_string& remappings, const std::string& name, uint32_t options)
init(remappings, name, options);
完整的函数实现:
void init(const M_string& remappings, const std::string& name, uint32_t options)
{
if (!g_atexit_registered)
{
g_atexit_registered = true;
atexit(atexitCallback);
}
if (!g_global_queue)
{
g_global_queue.reset(new CallbackQueue);
}
if (!g_initialized)
{
g_init_options = options;
g_ok = true;
ROSCONSOLE_AUTOINIT;
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
check_ipv6_environment();
network::init(remappings);
master::init(remappings);
this_node::init(name, remappings, options);
file_log::init(remappings);
param::init(remappings);
g_initialized = true;
}
}
一开始g_atexit_registered是false,所以会进入if语句里面,进入之后就赋值为true。
static bool g_atexit_registered = false;
组成程序退出的回调函数:
使用atexit函数注册的退出函数是在进程正常退出时,才会被调用。如果是因为收到信号signal而导致程序退出,如kill pid,那么atexit所注册的函数将不会被调用。这个函数是定义关联在signal.h中。signal.h是C标准函数库中的信号处理部分, 定义了程序执行时如何处理不同的信号。信号用作进程间通信, 报告异常行为(如除零)、用户的一些按键组合(如同时按下Ctrl与C键,产生信号SIGINT)。
atexit(atexitCallback);
atexitCallback回调函数打印一些消息到终端和调用shutdown结束节点
void atexitCallback()
{
if (ok() && !isShuttingDown())
{
ROSCPP_LOG_DEBUG("shutting down due to exit() or end of main() without cleanup of all NodeHandles");
g_started = false;
shutdown();
}
}
shutdown()函数的定义,解释在文件中注明
boost::recursive_mutex::scoped_lock lock(g_shutting_down_mutex);是一个递归锁。
递归锁:同一个线程在不解锁的情况下,可以多次获取锁定同一个递归锁,而且不会产生死锁。非递归锁:在不解锁的情况下,当同一个线程多次获取同一个递归锁时,会产生死锁。具体见博客。
void shutdown()
{
boost::recursive_mutex::scoped_lock lock(g_shutting_down_mutex);
if (g_shutting_down)
return;
else
g_shutting_down = true;
ros::console::shutdown();
g_global_queue->disable();
g_global_queue->clear();
if (g_internal_queue_thread.get_id() != boost::this_thread::get_id())
{
g_internal_queue_thread.join();
}
delete g_rosout_appender;
g_rosout_appender = 0;
if (g_started)
{
TopicManager::instance()->shutdown();
ServiceManager::instance()->shutdown();
PollManager::instance()->shutdown();
ConnectionManager::instance()->shutdown();
XMLRPCManager::instance()->shutdown();
}
g_started = false;
g_ok = false;
Time::shutdown();
}
接着init的函数继续分析
if (!g_global_queue)
{
g_global_queue.reset(new CallbackQueue);
}
g_global_queue的定义为
CallbackQueuePtr g_global_queue;
CallbackQueuePtr的定义为一个回调队列的共享指针
typedef boost::shared_ptr<CallbackQueue> CallbackQueuePtr;
reset()是boost::shared_ptr的一个函数,其中reset() 相当于释放当前所控制的对象
reset(T* p) 相当于释放当前所控制的对象,然后接管p所指的对象。
ROSCONSOLE_AUTOINIT是定义在console.h文件中。这个的主要作用是初始化日志输出的库,常并不需要直接调用,具体的内容如下:
#define ROSCONSOLE_AUTOINIT \
do \
{ \
if (ROS_UNLIKELY(!::ros::console::g_initialized)) \
{ \
::ros::console::initialize(); \
} \
} while(false)
ROSCONSOLE_AUTOINIT
由于各个节点之间采用TCP通信,为了保证一个节点退出时,其他节点不受影响,所以采用signal函数。这个函数会在Linux系统中运行。这时产生SIGPIPE信号时就不会中止程序,直接把这个信号忽略掉。
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
- SIGPIPE信号产生的规则:当一个进程向某个已收到RST的套接字执行写操作时,内核向该进程发送SIGPIPE信号。
- SIG_IGN是忽略信号。定义如下:
#define SIG_IGN (void (*)())1
chek_ipv6_environment的定义如下:
check_ipv6_environment();
void check_ipv6_environment() {
char* env_ipv6 = NULL;
#ifdef _MSC_VER
_dupenv_s(&env_ipv6, NULL, "ROS_IPV6");
#else
env_ipv6 = getenv("ROS_IPV6");
#endif
bool use_ipv6 = (env_ipv6 && strcmp(env_ipv6,"on") == 0);
TransportTCP::s_use_ipv6_ = use_ipv6;
XmlRpc::XmlRpcSocket::s_use_ipv6_ = use_ipv6;
}
_MSC_VER定义编译器的版本
MS VC++ 16.0 _MSC_VER = 1928 (Visual Studio 2019)
MS VC++ 15.0 _MSC_VER = 1910 (Visual Studio 2017)
MS VC++ 14.0 _MSC_VER = 1900 (Visual Studio 2015)
MS VC++ 12.0 _MSC_VER = 1800 (VisualStudio 2013)
MS VC++ 11.0 _MSC_VER = 1700 (VisualStudio 2012)
MS VC++ 10.0 _MSC_VER = 1600(VisualStudio 2010)
MS VC++ 9.0 _MSC_VER = 1500(VisualStudio 2008)
MS VC++ 8.0 _MSC_VER = 1400(VisualStudio 2005)
MS VC++ 7.1 _MSC_VER = 1310(VisualStudio 2003)
MS VC++ 7.0 _MSC_VER = 1300(VisualStudio .NET)
MS VC++ 6.0 _MSC_VER = 1200(VisualStudio 98)
MS VC++ 5.0 _MSC_VER = 1100(VisualStudio 97)
_dupenv_s和getenv的功能是一样的,返回一给定的环境变量值,环境变量名可大写或小写。如果指定的变量在环境中未定义,则返回一空串。只不过在不同编译环境中调用。getenv在vs编译环境会出现警告。
是否使用ipv6网络环境。
bool use_ipv6 = (env_ipv6 && strcmp(env_ipv6,"on") == 0);
设置通信网络环境
TransportTCP::s_use_ipv6_ = use_ipv6;
XmlRpc::XmlRpcSocket::s_use_ipv6_ = use_ipv6;
小结:check_ipv6_environment()函数的作用是获取系统中ipv6网络的环境变量并且根据配置进行修改通信网络的配置。
五个初始化函数,通信网络初始、主节点初始化、当前节点初始化、日志文件保存初始化、参数器初始化。
network::init(remappings);
master::init(remappings);
this_node::init(name, remappings, options);
file_log::init(remappings);
param::init(remappings);
定义如下:
namespace master
{
void init(const M_string& remappings);
}
namespace this_node
{
void init(const std::string& names, const M_string& remappings, uint32_t options);
}
namespace network
{
void init(const M_string& remappings);
}
namespace param
{
void init(const M_string& remappings);
}
namespace file_log
{
void init(const M_string& remappings);
}
网络初始化:
void init(const M_string& remappings)
{
M_string::const_iterator it = remappings.find("__hostname");
if (it != remappings.end())
{
g_host = it->second;
}
else
{
it = remappings.find("__ip");
if (it != remappings.end())
{
g_host = it->second;
}
}
it = remappings.find("__tcpros_server_port");
if (it != remappings.end())
{
try
{
g_tcpros_server_port = boost::lexical_cast<uint16_t>(it->second);
}
catch (boost::bad_lexical_cast&)
{
throw ros::InvalidPortException("__tcpros_server_port [" + it->second + "] was not specified as a number within the 0-65535 range");
}
}
if (g_host.empty())
{
g_host = determineHost();
}
}
determineHost()的定义如下:
std::string determineHost()
{
std::string ip_env;
if ( get_environment_variable(ip_env, "ROS_HOSTNAME")) {
ROSCPP_LOG_DEBUG( "determineIP: using value of ROS_HOSTNAME:%s:", ip_env.c_str());
if (ip_env.size() == 0)
{
ROS_WARN("invalid ROS_HOSTNAME (an empty string)");
}
return ip_env;
}
if ( get_environment_variable(ip_env, "ROS_IP")) {
ROSCPP_LOG_DEBUG( "determineIP: using value of ROS_IP:%s:", ip_env.c_str());
if (ip_env.size() == 0)
{
ROS_WARN("invalid ROS_IP (an empty string)");
}
return ip_env;
}
char host[1024];
memset(host,0,sizeof(host));
if(gethostname(host,sizeof(host)-1) != 0)
{
ROS_ERROR("determineIP: gethostname failed");
}
else if(strlen(host) && strcmp("localhost", host))
{
return std::string(host);
}
#ifdef HAVE_IFADDRS_H
struct ifaddrs *ifa = NULL, *ifp = NULL;
int rc;
if ((rc = getifaddrs(&ifp)) < 0)
{
ROS_FATAL("error in getifaddrs: [%s]", strerror(rc));
ROS_BREAK();
}
char preferred_ip[200] = {0};
for (ifa = ifp; ifa; ifa = ifa->ifa_next)
{
char ip_[200];
socklen_t salen;
if (!ifa->ifa_addr)
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
salen = sizeof(struct sockaddr_in);
else if (ifa->ifa_addr->sa_family == AF_INET6)
salen = sizeof(struct sockaddr_in6);
else
continue;
if (getnameinfo(ifa->ifa_addr, salen, ip_, sizeof(ip_), NULL, 0,
NI_NUMERICHOST) < 0)
{
ROSCPP_LOG_DEBUG( "getnameinfo couldn't get the ip of interface [%s]", ifa->ifa_name);
continue;
}
if (!strcmp("127.0.0.1", ip_) || strchr(ip_,':'))
continue;
if (ifa->ifa_addr->sa_family == AF_INET6 && !preferred_ip[0])
strcpy(preferred_ip, ip_);
else if (isPrivateIP(ip_) && !preferred_ip[0])
strcpy(preferred_ip, ip_);
else if (!isPrivateIP(ip_) &&
(isPrivateIP(preferred_ip) || !preferred_ip[0]))
strcpy(preferred_ip, ip_);
}
freeifaddrs(ifp);
if (!preferred_ip[0])
{
ROS_ERROR( "Couldn't find a preferred IP via the getifaddrs() call; I'm assuming that your IP "
"address is 127.0.0.1. This should work for local processes, "
"but will almost certainly not work if you have remote processes."
"Report to the ROS development team to seek a fix.");
return std::string("127.0.0.1");
}
ROSCPP_LOG_DEBUG( "preferred IP is guessed to be %s", preferred_ip);
return std::string(preferred_ip);
#else
ROS_ERROR( "You don't have the getifaddrs() call; I'm assuming that your IP "
"address is 127.0.0.1. This should work for local processes, "
"but will almost certainly not work if you have remote processes."
"Report to the ROS development team to seek a fix.");
return std::string("127.0.0.1");
#endif
}
master的初始化代码如下:
void init(const M_string& remappings)
{
M_string::const_iterator it = remappings.find("__master");
if (it != remappings.end())
{
g_uri = it->second;
}
if (g_uri.empty())
{
char *master_uri_env = NULL;
#ifdef _MSC_VER
_dupenv_s(&master_uri_env, NULL, "ROS_MASTER_URI");
#else
master_uri_env = getenv("ROS_MASTER_URI");
#endif
if (!master_uri_env)
{
ROS_FATAL( "ROS_MASTER_URI is not defined in the environment. Either " \
"type the following or (preferrably) add this to your " \
"~/.bashrc file in order set up your " \
"local machine as a ROS master:\n\n" \
"export ROS_MASTER_URI=http://localhost:11311\n\n" \
"then, type 'roscore' in another shell to actually launch " \
"the master program.");
ROS_BREAK();
}
g_uri = master_uri_env;
#ifdef _MSC_VER
free(master_uri_env);
#endif
}
if (!network::splitURI(g_uri, g_host, g_port))
{
ROS_FATAL( "Couldn't parse the master URI [%s] into a host:port pair.", g_uri.c_str());
ROS_BREAK();
}
}
network::splitURI的定义如下:
bool splitURI(const std::string& uri, std::string& host, uint32_t& port)
{
if (uri.substr(0, 7) == std::string("http://"))
host = uri.substr(7);
else if (uri.substr(0, 9) == std::string("rosrpc://"))
host = uri.substr(9);
std::string::size_type colon_pos = host.find_first_of(":");
if (colon_pos == std::string::npos)
return false;
std::string port_str = host.substr(colon_pos+1);
std::string::size_type slash_pos = port_str.find_first_of("/");
if (slash_pos != std::string::npos)
port_str = port_str.erase(slash_pos);
port = atoi(port_str.c_str());
host = host.erase(colon_pos);
return true;
}
这个节点初始化代码:
void ThisNode::init(const std::string& name, const M_string& remappings, uint32_t options)
{
char *ns_env = NULL;
#ifdef _MSC_VER
_dupenv_s(&ns_env, NULL, "ROS_NAMESPACE");
#else
ns_env = getenv("ROS_NAMESPACE");
#endif
if (ns_env)
{
namespace_ = ns_env;
#ifdef _MSC_VER
free(ns_env);
#endif
}
if (name.empty()) {
throw InvalidNameException("The node name must not be empty");
}
name_ = name;
bool disable_anon = false;
M_string::const_iterator it = remappings.find("__name");
if (it != remappings.end())
{
name_ = it->second;
disable_anon = true;
}
it = remappings.find("__ns");
if (it != remappings.end())
{
namespace_ = it->second;
}
if (namespace_.empty())
{
namespace_ = "/";
}
namespace_ = (namespace_ == "/")
? std::string("/")
: ("/" + namespace_)
;
std::string error;
if (!names::validate(namespace_, error))
{
std::stringstream ss;
ss << "Namespace [" << namespace_ << "] is invalid: " << error;
throw InvalidNameException(ss.str());
}
names::init(remappings);
if (name_.find("/") != std::string::npos)
{
throw InvalidNodeNameException(name_, "node names cannot contain /");
}
if (name_.find("~") != std::string::npos)
{
throw InvalidNodeNameException(name_, "node names cannot contain ~");
}
name_ = names::resolve(namespace_, name_);
if (options & init_options::AnonymousName && !disable_anon)
{
char buf[200];
snprintf(buf, sizeof(buf), "_%llu", (unsigned long long)WallTime::now().toNSec());
name_ += buf;
}
ros::console::setFixedFilterToken("node", name_);
}
日记输出的初始化代码:
void init(const M_string& remappings)
{
std::string log_file_name;
M_string::const_iterator it = remappings.find("__log");
if (it != remappings.end())
{
log_file_name = it->second;
}
{
if (log_file_name.empty())
{
pid_t pid = getpid();
std::string ros_log_env;
if ( get_environment_variable(ros_log_env, "ROS_LOG_DIR"))
{
log_file_name = ros_log_env + std::string("/");
}
else
{
if ( get_environment_variable(ros_log_env, "ROS_HOME"))
{
log_file_name = ros_log_env + std::string("/log/");
}
else
{
if( get_environment_variable(ros_log_env, "HOME") )
{
std::string dotros = ros_log_env + std::string("/.ros/");
fs::create_directory(dotros);
log_file_name = dotros + "log/";
fs::create_directory(log_file_name);
}
}
}
for (size_t i = 1; i < this_node::getName().length(); i++)
{
if (!isalnum(this_node::getName()[i]))
{
log_file_name += '_';
}
else
{
log_file_name += this_node::getName()[i];
}
}
char pid_str[100];
snprintf(pid_str, sizeof(pid_str), "%d", pid);
log_file_name += std::string("_") + std::string(pid_str) + std::string(".log");
}
log_file_name = fs::system_complete(log_file_name).string();
g_log_directory = fs::path(log_file_name).parent_path().string();
}
}
参数器初始化代码:
void init(const M_string& remappings)
{
M_string::const_iterator it = remappings.begin();
M_string::const_iterator end = remappings.end();
for (; it != end; ++it)
{
const std::string& name = it->first;
const std::string& param = it->second;
if (name.size() < 2)
{
continue;
}
if (name[0] == '_' && name[1] != '_')
{
std::string local_name = "~" + name.substr(1);
bool success = false;
try
{
int32_t i = boost::lexical_cast<int32_t>(param);
ros::param::set(names::resolve(local_name), i);
success = true;
}
catch (boost::bad_lexical_cast&)
{
}
if (success)
{
continue;
}
try
{
double d = boost::lexical_cast<double>(param);
ros::param::set(names::resolve(local_name), d);
success = true;
}
catch (boost::bad_lexical_cast&)
{
}
if (success)
{
continue;
}
if (param == "true" || param == "True" || param == "TRUE")
{
ros::param::set(names::resolve(local_name), true);
}
else if (param == "false" || param == "False" || param == "FALSE")
{
ros::param::set(names::resolve(local_name), false);
}
else
{
ros::param::set(names::resolve(local_name), param);
}
}
}
XMLRPCManager::instance()->bind("paramUpdate", paramUpdateCallback);
}
到此一个节点基础的初始化就完成,网络、主节点等模块将下篇博客详细分析。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)