您的位置:首页 > Web前端 > Node.js

ROS源代码阅读(3):ROS程序的初始化——this_node::init()

2017-09-18 23:03 851 查看
接着上一篇博文ROS程序的初始化——从ros:init()出发,我们接着来分析ROS程序的初始化问题。在此文中我们探讨的是节点环境的初始化——this_node::init()

this_node::init()函数的源代码在
./src/ros_comm/roscpp/src/libros/this_node.cpp
中,具体实现如下:

//./src/ros_comm/roscpp/src/libros/this_node.cppvoid init(const std::string& name, const M_string& remappings, uint32_t options)
{
ThisNode::instance().init(name, remappings, options);
}


其中,ThisNode是文件
./src/ros_comm/roscpp/src/libros/this_node.cpp
中定义的一个类,该类的具体定义如下:

//./src/ros_comm/roscpp/src/libros/this_node.cppclass ThisNode
{
std::string name_;
std::string namespace_;

ThisNode() : name_("empty") {}//构造对象时将name_置为空

public:
static ThisNode& instance()
{
static ThisNode singleton;//整个ROS程序只有singleton一个拷贝,详见编程中的“单例模式”。
return singleton;
}

const std::string& getName() const
{
return name_;
}

const std::string& getNamespace() const
{
return namespace_;
}

void init(const std::string& name, const M_string& remappings, uint32_t options);
};


this_node::init()函数实际上直接调用的是ThisNode类中的
void init(const std::string& name, const M_string& remappings, uint32_t options);
函数。该函数的定义如下:

//./src/ros_comm/roscpp/src/libros/this_node.cppvoid 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");//获取ROS_NAMESPACE的环境变量名
#endif

if (ns_env)//如果环境变量ns_env已被赋值
{
namespace_ = ns_env;//将ROS_NAMESPACE的环境变量名赋值给namespace_
//namespace_是类ThisNode的成员变量
#ifdef _MSC_VER
free(ns_env);
#endif
}

//检测通过参数传入的节点名不能为空
if (name.empty()) {
throw InvalidNameException("The node name must not be empty");
}

name_ = name; //将传入的节点名赋值给变量name_
//name_是类ThisNode的成员变量

bool disable_anon = false;
//在输入参数remappings查找键为"__name"的项
M_string::const_iterator it = remappings.find("__name");
if (it != remappings.end())//如果找到了
{
name_ = it->second;//将对应项的值赋值给name_
disable_anon = true;
}
//在输入参数remappings查找键为"__ns"的项
it = remappings.find("__ns");//如果找到了
if (it != remappings.end())
{
namespace_ = it->second;//将对应项的值赋值给变量namespace_
}

if (namespace_.empty())//如果namespace_为空
{
namespace_ = "/";
}

namespace_ = (namespace_ == "/")
? std::string("/")
: ("/" + namespace_)
;

std::string error;
//对照命名规则检查namespace_,看看是否合法。
if (!names::validate(namespace_, error))
{
std::stringstream ss;
ss << "Namespace [" << namespace_ << "] is invalid: " << error;
throw InvalidNameException(ss.str());
}

// names must be initialized here, because it requires the namespace to already be known so that it can properly resolve names.
// It must be done before we resolve g_name, because otherwise the name will not get remapped.
names::init(remappings);//将remappings映射为g_remappings和g_unresolved_remappings两个变量
//检查name_的合法性
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_);
}


从代码可以看出,该函数完成了以下几个功能:

获取ROS_NAMESPACE的环境变量名;

给变量name_赋值,并进行一些格式化处理。name_是类ThisNode的成员变量;

给变量namespace_赋值,并进行一些格式化处理。namespace_是类ThisNode的成员变量;

根据类ThisNode的定义,该类的成员变量就只有name_和namespace_两个变量。因此,该函数可以看做是根据输入参数,对ThisNode的对象进行初始化。

而根据ThisNode::instance()函数,该类在程序中只有唯一的一个对象。即调用this_node::init()的时候完成对该类唯一对象的初始化。

另外,上述函数调用了

names::validate(namespace_, error)(上述代码第57行)

names::init(remappings)(上述代码第66行)

ros::console::setFixedFilterToken(上述代码第86行)

三个函数。为了更好理解代码,我们下面对这三个函数做一简述。

插曲1:names::validate(namespace_, error)

上述代码调用了函数
names::validate(namespace_, error)
,该函数定义在
./src/ros_comm/roscpp/src/libros/names.cpp
中。具体实现如下。

bool validate(const std::string& name, std::string& error)
{
if (name.empty())
{
return true; //如果name为空,则返回true
}

//检查首字符,首字符只能是~ / 或 alpha
char c = name[0];
if (!isalpha(c) && c != '/' && c != '~')
{
std::stringstream ss;
ss << "Character [" << c << "] is not valid as the first character in Graph Resource Name [" << name << "].  Valid characters are a-z, A-Z, / and in some cases ~.";
error = ss.str();
return false;
}

//逐个检查name中的每个字符是否为合法字符
for (size_t i = 1; i < name.size(); ++i)
{
c = name[i];
if (!isValidCharInName(c))
{
std::stringstream ss;
ss << "Character [" << c << "] at element [" << i << "] is not valid in Graph Resource Name [" << name <<"].  Valid characters are a-z, A-Z, 0-9, / and _.";
error = ss.str();
return false;
}
}
return true;
}


插曲2:names::init(remappings)

该函数定义在
./src/ros_comm/roscpp/src/libros/names.cpp
文件中,作用是将remappings映射为g_remappings和g_unresolved_remappings两个变量,其中g_remappings是按照一定规则整理过的remappings,而g_unresolved_remappings是初始传入的remappings参数

//./src/ros_comm/roscpp/src/libros/names.cppvoid init(const M_string& remappings)
{
//该函数的作用是将remappings映射为g_remappings和g_unresolved_remappings两个变量
M_string::const_iterator it = remappings.begin();
M_string::const_iterator end = remappings.end();
for (; it != end; ++it) //遍历M_string中的每个元素
{
const std::string& left = it->first; //left为键
const std::string& right = it->second; //right为值
//键不为空 且 键的第一个字符不为“_” 且 键不等于ThisNode对象的name_成员变量
if (!left.empty() && left[0] != '_' && left != this_node::getName())
{
std::string resolved_left = resolve(left, false);
std::string resolved_right = resolve(right, false);
g_remappings[resolved_left] = resolved_right;
g_unresolved_remappings[left] = right;
}
}
}


其中调用了resolve()函数,该函数也定义在
./src/ros_comm/roscpp/src/libros/names.cpp
中,执行一些简答的格式化操作,在此不进行详述。

插曲3:ros::console::setFixedFilterToken

该文件的实现在
./src/ros_comm/rosconsole/src/rosconsole/rosconsole.cpp
文件中,具体代码如下:

void setFixedFilterToken(const std::string& key, const std::string& val)
{
g_extra_fixed_tokens[key] = val;
}


从代码可以看出,该函数主要是对变量g_extra_fixed_tokens进行赋值。

总结

当初始化函数this_node::init()被ros::init()调用时,实际上调用了ROS程序中ThisNode类唯一的实例中的init(name, remappings, options)函数,作用是对该唯一的实例进行初始化。

该函数的具体作用如下:

1. 获取ROS_NAMESPACE的环境变量名;

2. 给变量name_赋值,并进行一些格式化处理。name_是类ThisNode的成员变量;

3. 给namespace_赋值,并进行一些格式化处理。namespace_是类ThisNode的成员变量;

4. 将remappings映射为g_remappings和g_unresolved_remappings两个变量;

ros::init()函数的五个主要初始化操作中的第三个,后续我们将解析剩余的两个初始化操作file_log::init和param::init。然后,对ros::init()函数做一个综合的分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐