在我们的消息解析模块中,使用到了服务器的库文件,主要就是 predicate.h
和 parameterlist.h
,它们的作用就是用来处理 S-expression
(S表达式)的。
leisland
撰写和编辑,原作者陈银华
(2010-05-13)孙振兴
(2021-07-10)仅用于学习材料
。先看 parameterList,它的定义是:
public:
typedef std::vector<boost::any> TVector;
protected:
TVector mList;
解释:
class ParameterList manages a list of values. Boost::Any is used as a typesafe
container to realize a sequence of values of arbitrary types.
它其实是一个数组,数组的每一个元素为任意类型
。这个数组其实是 vector
容器,容器每一个元素的类型是 Boost::Any
解释:
class Predicate encapsulates a predicate name together with its list of
parameters.
一个 predicate
封装了判别式(predicate)的名称和这个判别式的所有数据。这个判别式就是一个 S表达式
。
我们先解释什么是 S-expression
(S-表达式)
一个S表达式是一对左右括号之间的信息集合,里面的内容包括数字、符号、字符串。下图所示的是S表达式的结构。它广泛的应用在Lisp语言的编程中,一般用来进行编码和存放数据。
一个S表达式通常有以下格式
利用 S-expressions
的优点在于其数据格式易于分析,简洁的语法在一定程度上为我们的调试提供了可读性。并且,S-expressions
还便于增加新的传感器信息。
S-expressions
还便于增加新的传感器信息。
这就是我们为什么要搞清楚消息解析原理,一旦服务器增加新的传感器消息,我们现在的代码是不能够解析出来的。
这就要求我们修改消息解析的代码,以便取出新的数据。
一个判别式就是一个 S-expression
!
我们还是到服务器发给 agent
的消息中取个例子来分析分析吧!服务器发给 agent
的时间消息是这样的:
(time (now 13.62))
而这就是一个S-expression,解析它时,它就对应一个 predicate。在 predicate 的定义中有以下这两行代码:
/** the name of this predicate */
std::string name;
/** the list of parameter values */
zeitgeist::ParameterList parameter;
我们可以很清楚的发现,一个 predicate 包含该 predicate 的名字和 parameterList。用上面的例子来解释,就是 name 的值是 time
,parameter 是一个数组,它有两个数组元素,分别是 now
和14.62
。parameter 数组的两个元素,一个是字符串型(string),另一个却是浮点型(float),这确实很奇妙!
Predicate中需要我们知道的函数:
bool DescentList(Iterator& iter) const;
void GetValue(const Iterator& iter, T& value) const;
bool FindParameter(Iterator& iter, const std::string& name) const;
void AdvanceValue(Iterator& iter, T& value) const;
DescentList
这个函数的作用是把 predicate 中 parameter 的数组元素转变成 parameterList,并使得 iter 指向这个 parameterList。当然,这是对于parameter 中每个元素都是 parameterList 而言的,如果是上面的那个例子,是没必要使用这个函数的。GetValue
可以把iter所指向的parameterList中的标记元素取出来,若是上面的例子,那么取出来的就是“now”。FindParameter
依据 name 的标记,使 iter 指向 name 之后的数据,也就是找到 name 所对应的数据。AdvanceValue
取出 iter 所指向的数据,并存放在 value 中。消息解析器(mparser)
首先将服务器的消息(massage)
解析成 predicatelist(判别式数组)
,每一个数组元素都是一个 predicate(判别式)
。
在服务器的消息中,每一对最外层的括号就会对应一个 predicate,因此,服务器消息中有多少对这样的括号, predicatelist 就有多少个 predicate元素。而在这样的一对括号里面,有多少对括号,就对应多少 parameterList。
这是不是像绕口令?还是让我们看个例子吧!
假设服务器的视觉消息是这样的:
(see
(G1L(pol 6.27 74.18 -6.37))
(G2R(pol 6.05 87.14 -6.61))
)
整个 see
,也就是最外层的这对括号,对应一个 predicate,其层次结构如下图所示:
服务器的消息有不同类型,包括:time,GSGYR,See,HJ,FRP等。根据其首字母,基本可将消息区分出来;如不能区分,则再比较第二字母,以此类推。
如,以'G'开头的有 GS 和 GYR,此时比较它们的第一个字符并不能区分这两种消息,所以还要比较第二个字符。消息解析的主循环就是依据以上原则进行工作的:
shared_ptr<PredicateList> predList = mParser->Parse(message);
调用 Parse
的作用就是把服务器的消息打包成许多 predicate
,然后放在一个 PredicateList
中。以下是消息解析的主循环,它逐个处理 PredicateList
中的每个 predicate
:
if (predList.get() != 0)
{
PredicateList &list = *predList;
for (
PredicateList::TList::const_iterator iter = list.begin();
iter != list.end();
++iter)
{
const Predicate &predicate = (*iter);
// check for a joint percept
switch (predicate.name[0])
{
case 'h': // hear
ParseHearInfo(predicate);
break;
case 't': //get simTime
ParseServerTime(predicate);
break;
case 'G': //Parse Game state
switch (predicate.name[1])
{
case 'S': //paring GameState
ParseGameState(predicate);
break;
case 'Y': //Parsing GYR
ParseGYR(predicate);
break;
default:
break;
}
break;
case 'S': //parsing VIsion
ParseVision(predicate);
break;
case 'H': // hinge joint (HJ)
ParseHingeJointInfo(predicate);
break;
case 'F': //paring FRP sensor
ParseFRP(predicate);
break;
default:
break;
}
}
}
其中每个 case
调用函数的处理过程大体是这样的,只是有些许的差别:
逐一取出 predicate
中的每一个 parameterList,对每个 parameterList 作如下操作:
G1L
。对应的函数为:GetValue(const Iterator&iter, T&value) const;G1L
找到 map容器
中对应的映射;pol
中的位置,将其后的三个位置提取到步骤2中的
映射内。对应的函数为:FindParameter
和 AdvanceValue
;predicate
的头文件目录:lib/oxygen/gamecontrolserver/predicate.h
parameterList
的头文件目录:lib/zeitgeist/parameterList.h
parser
的头文件目录:utility/sfsexp/parser.h
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。