NML详解

1.NML 工程安装

NML是RCS库中的一部分,NML英文全称是neutral message language。它是由美国国家标准与技术研究院(NIST,National Institute of Standard and Technology)针对分布式通信而设计的一种通信协议与方式。

1.1 RCS安装

以下方法针对的是Ubuntu系统

确认依赖环境:boost(常用版本有1.54,1.55和1.58)和Java,如果没有Java,需要按以下步骤安装java。

此处安装java版本为jdk1.8.0_281

https://blog.csdn.net/mucaoyx/article/details/82949450

配置java环境变量

这里是将环境变量配置在etc/profile,即为所有用户配置JDK环境,使用命令sudo gedit /etc/profile打开/etc/profile文件,在末尾添加以下几行文字:

#set java environment

export JAVA_HOME=/usr/lib/jdk/jdk1.8.0_281

export JRE_HOME=${JAVA_HOME}/jre    

export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib     

export PATH=${JAVA_HOME}/bin:$PATH 

使用 source /etc/profile 使配置立马生效

java安装完成后,建立软连接

1
2
3
4
5
6
update-alternatives --install /usr/bin/java
java /opt/jdk1.7.0_79/bin/java 300
update-alternatives --install
/usr/bin/javac javac /opt/jdk1.7.0_79/bin/javac 300
update-alternatives --install /usr/bin/jar
jar /opt/jdk1.7.0_79/bin/jar 300

测试 java -version

1. 2 NML安装

https://www.cnblogs.com/AIpower/p/12743445.html

http://www.isd.mel.nist.gov/projects/rcslib/rcslib-2014.04.29.tar.gz

RCS安装包

  1. 解压安装包
  2. 执行下列语句
cd rcslib-2014.04.29(对应安装包名字)
./configure -enable-ALLJAVA
[修改Makefile中的prefix选项为 /opt/rcslib/build,此处将生成lib与include]

[注意在Ubuntu 18.04中会报“unable to find string literal operator”错误,
需要在该文件(cms_xml_up.cc)中定位到此语句,并将该语句中“”引号的前后各加一个空格:xmlXsdType(xsdNs,\space "xsd:" \space X)]

make 
make check
make install(可能需要root权限)

NML的配置

NML配置文件主要包括四个部分,分别是:

  • 注释
  • 消息
  • 进程
  • server

1.注释

注释以‘#’开头,可以看到有一行以#开头,

为了更好的表示消息的写法,特意加了该注释

2.消息(buffer)

消息对应在src/message里面的文件,只有定义了消息的头文件,才能够被其他程序调用。只有此处配置正确,才能保证消息的正常收发。

对于代表buffer的.hh文件,可用Makefile文件对message进行修改和编译,每次修改.hh文件都需要重新编译,编译生成新的/Test/lib/libTest.a文件.

Message名称以n结尾,如MessageAn.hh MessageBn.hh MessageCn.hh等.这是在message文件夹里的Makefile中约定的,方便统一管理.而在.nml配置文件中,我们将后缀n略去.

消息以B开头

如何编写和定义消息

# Buffers
# Name Type Host size neut RPC# buffer$ MP ...
B MessageA SHMEM 172.23.100.205 1024 0 * 1001 * 50001 bsem=5421 TCP=5421 xdr
  • Name是消息名称,对应到src/message里面的文件.
  • Type表示的是消息的使用形式,包含三种方式:
  1. SHMEM(share memory) TCP
  2. GLOBMEM(global memory) UDP
  3. LOCMEM(local memory) RPC
  • Host表示的是buffer所在的主机名,如果消息只是在同一台机器上传递,则最好写localhost.需要特别注意的是,如果是不同机器传递某一个消息,需要给消息写一个server,来负责传递,则这里要写的是该消息的server所在的主机名(或主机IP地址)。这里可以看到,host1和host4是有通信关系的,很多消息都在这两台机子中间传递。因此, 规划模块在host1上,与之相关的server也在host1上,因此此处的Host也是写host1。
  • size表示的是消息的大小,单位字节,这里要根据msg定义的数据结构来确认

test.png

  • neut 0或1都可以,区别是什么官方文档并未介绍清楚,如果没有处理器不兼容,则设0。通常取0即可。
  • RPC# 已经被官方弃用,因此统一用符号来占位。
  • buffer# buffer的序列号,需要保证唯一即可。
  • MP:max_procs,可以用符号占位。
  • [Type-spec data]:对于SHMEM,需要一个唯一的共享内存键。后面接着“TCP=”,后面的数字必须大于1024。后面接着"bsem=",后面的数字与TCP不同即可。

这里要注意的是,如果用了两个不同的nml文件,如果有共同的buffer,后面的[Type-spec data]必须是一致的,否则无法通信。

3.进程(Process)

进程以P开头.

# Name Buffer Type Host Ops server timeout master cnum
# ModuleA
P ModuleA MessageA LOCAL 172.23.100.205 R 0 0.1 1 1 waitformaster
  • Name是进程名称,一般对应到bin里面的可运行程序,有可能一个程序有多个进程.
  • Buffer表示的是该进程使用的消息名称,必须在Buffer中定义
  • Type只能是LOCAL,REMOTE,AUTO
  • Host表示是进程所在主机IP地址
  • Ops表示进程对消息的操作方式,R(读),W(写),RW(既读又写)
  • server对于svr而言为1,否则为0;
  • timeout互斥时间通常写为0.1
  • master 表示指定消息的传递指定哪一个process是master,注意master可以不唯一.
  • cnum除非GLOBMEM,否则没用.

4.Server

Server是一种特殊的进程,其写法与进程类似. 在/src/server下可用Makefile文件对server进行修改和编译,每次修改.cc文件都需要重新编译,编译生成心得/Test/bin/*svr文件

#Server
# Name Buffer Type Host 0ps server timeout master cnum
P ServerAsvr MessageA LOCAL 172.23.100.210 RW 1 0.1 1 51

注意:

server的Type都是LOCAL,Host是server程序所在主机地址。Ops一般为RW操作,server为1,master为1即可。

3. 程序中调用NML库函数

内容分两个部分:如何用C++来编写NMLmessage,主程序中如何使用NML来通信

3.1 编写C++ NMLmessage

NML message存放在src文件夹下,通常与应用程序在同一级目录下.这种目录结构最小化修改文件目录.

message文件夹内需要copy以下几个基本的文件:

--Makefile

--NMLmsgExn.cc

--NMLmsgExn.hh

--CommonDefinitionX可用可不用,

  • Makefile文件,该文件需要针对头文件、目标文件和NML_CODEGEN的安装位置,进行修改.通常NML_CODEGEN的安装位置为:/usr/local/bin/CodeGenCmdLine.jar

    如果编译message时,出现unable to access jarfile,这种情况说明CodeGenCmdLine.jar位置有误.

    clean:
      -\rm -f *.o lib*.a lib*.so lib*.la *.lib *.ddll
    
  • CommonDefinitionX和NMLmsgExn.hh不用改,是NML通信使用的模块

  • 自定义的message,以XXXXn.hh结尾,以表明是用NML通信

    需要引入的头文件包括:

    1
    2
    3
    4
    5
    6
    7
    
    // Prevent Multiple Inclusion
    #ifndef MESSAGEAN_HH
    #define MESSAGEAN_HH
    // Include Files
    #include "rcs.hh"     // Common RCS definitions
    #include "NMLmsgExn.hh"
    #include "CommonDefinitionX.hh"
    

    定义MESSAGE_MSG_TYPE

    // Define the integer type ids.
    #define MESSAGEA_MSG_TYPE 40101
    

    接下来则是消息的定义,可以根据自己的需要,自定义数据结构

    struct UserType{
        int int_array[2];
        std::string v_str;
    };
    // Define the NML Message Classes
    class MESSAGEA_MSG : public NMLmsgEx
    {
    public:
        //Constructor
        MESSAGEA_MSG();
        // CMS Update Function
        void update(CMS *);
        // Place custom variables here.
        char v_char;
        double v_double;
        UserType usertype_data;
    };
    // Declare NML format function
    extern int MessageAFormat(NMLTYPE, void *, CMS *);
    #endif     // MESSAGEAN_HH
    

3.2 主程序采用NML进行消息传递

  1. NML通信模块启用

在主程序中需要引用使用的NMLmessage的头文件.在启用NML时使用,使用以下语句:

1
2
set_rcs_print_destination(RCS_PRINT_TO_STDOUT);
nml_start();

在程序通信结束后,需要清空和关闭NML:

nml_cleanup();
  1. NML通信类的初始化

NML的使用需要引入nml配置文件,关于配置:

1
NML * MESSAGE_CHANNEL = new NML(formatFunction,"buffer","process","condig.nml");

参数buffer对应配置文件中B的名称,process对应P的名称,config.nml对应nml的文件名

formatFunction在nml message文件的最后有同名的定义

如果配置文件中Process对应的master为1,NML 通信类初始化后,才会启动buffer的master。(todo)

  1. NML通信消息的初始化

针对写消息的模块,采用

Message_data = new MESSAGE_MSG;

针对读消息的模块,采用

Message_data = (MESSAGEC_MSG *) MESSAGE_CHANNEL->get_address();

接收的buffer,不需要重新new MESSAGEA_MSG。发送的Buffer,需要在构造函数中new MESSAGEA_MSG开辟新空间。

  1. 消息读写

消息读采用以下命令判断接收是否正常,如正常,可以直接使用buffer中定义变量:

MESSAGE_CHANNEL ->blocking_read(0.2)

消息写采用以下命令:

MESSAGE_CHANNEL ->write(Message_data)


NML的构建

在Qt中进行NML工程的构建

需要利用qmake进行

.pro文件中链接语句bin和lib位置需要修改

src中将ModuleA和ModuleB分别进行编译运行

message中的设置前文中已经提到,不复议

源码部分在LanePro代码解析中再进行


updatedupdated2021-07-072021-07-07