解析ros init(二)

this_node::init

命名空间names是在这里初始化的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
void init(const std::string& name, const M_string& remappings, uint32_t options)
{
ThisNode::instance().init(name, remappings, options);
}
// ThisNode是一个类,instance()是单例模式
static ThisNode& instance()
{
static ThisNode singleton;
return singleton;
}

// 主要实现在这里
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; // namespace_是成员变量
#ifdef _MSC_VER
free(ns_env);
#endif
}
if (name.empty()) { // 节点名不能为空
throw InvalidNameException("The node name must not be empty");
}

name_ = name; // 节点名赋值,name_也是类成员变量,初始值为"empty"

bool disable_anon = false;
//在输入参数remappings查找键为"__name"的项
M_string::const_iterator it = remappings.find("__name");
if (it != remappings.end())
{
name_ = it->second;
disable_anon = true;
}
//在输入参数remappings查找键为"__ns"的项
it = remappings.find("__ns");
if (it != remappings.end())
{
namespace_ = it->second;
}
// 这里可以看出ROS_NAMESPACE不是必要的
if (namespace_.empty())
{
namespace_ = "/";
}
// 如果在上面赋值为 / ,最终就是 / ;否则就是 /+namespace
namespace_ = (namespace_ == "/")
? std::string("/")
: ("/" + namespace_)
;

std::string error;
// 对命名空间进行验证,这肯定又是个字符串处理函数
// 检查首字符,首字符只能是~ / 或 alpha,逐个检查name中的每个字符是否为合法字符
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.

// 将remappings映射为g_remappings和g_unresolved_remappings两个变量
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_);
// 如果初始化时的options选择的时匿名节点,那么在节点名后加UNIX时间戳,单位是纳秒
if (options & init_options::AnonymousName && !disable_anon)
{
char buf[200];
// 其实是ros::WallTime::now() 返回当前时间的纳秒表示
snprintf(buf, sizeof(buf), "_%llu", (unsigned long long)WallTime::now().toNSec());
name_ += buf;
}
//把节点和名字联系起来
ros::console::setFixedFilterToken("node", name_);
}

最后的函数是:

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

主要是对变量g_extra_fixed_tokens进行赋值

file_log::init

file_log就是个命名空间,函数定义在./src/ros_comm/roscpp/src/libros/file_log.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
void init(const M_string& remappings)
{
std::string log_file_name;
M_string::const_iterator it = remappings.find("__log");
//在remappings中找到键为"__log"的项
if (it != remappings.end())
{
log_file_name = it->second; //如果找到了,将对应的值赋值给log_file_name
}

{
// Log filename can be specified on the command line through __log
// If it's been set, don't create our own name
if (log_file_name.empty())
{
// Setup the logfile appender
// Can't do this in rosconsole because the node name is not known
pid_t pid = getpid(); //获取当前进程号
std::string ros_log_env;
//获取"ROS_LOG_DIR"
if ( get_environment_variable(ros_log_env, "ROS_LOG_DIR"))
{
log_file_name = ros_log_env + std::string("/");
}
else //如果不存在"ROS_LOG_DIR"这个环境变量
{ //获取"ROS_HOME"的环境变量值
if ( get_environment_variable(ros_log_env, "ROS_HOME"))
{
log_file_name = ros_log_env + std::string("/log/");
}
else //如果不存在环境变量"ROS_HOME"
{
// 无法跨平台?
// 如果没有设置以上环境变量,日志最终放在 ~/.ros/log
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);
}
}
}

// 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];
}
}
// 变成了 节点名_pid_log
char pid_str[100];
snprintf(pid_str, sizeof(pid_str), "%d", pid); //将pid以整形变量的形式写入pid_str
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();
}
}

这个函数其实就是确定日志存放目录和日志名称,例如:

问题是为什么有的节点最后还要加上时间戳?

参考:
ros time.h