NML工程入門-三步使用
撰寫人:李峻翔
特別鳴謝:劉伯凱
版本:V1.0
第二步、配置NML文件(configuration file)
第三步、程序中調用NML庫函數
編寫C++NMLmessage
主程序采用NML進行消息傳遞
NML-demo
三模塊雙機通信
NML常見報錯和解決思路
NML是RCS庫中的一部分,NML英文全稱是neutral message language。它是由美國國家標准與技術研究院(NIST,National Institute of Standard and Technology)針對分布式通信而設計的一種通信協議與方式。
無人車開發過程中,使用NML通信,並不需要對NML了解很深。由於目前可參考的資料並不多,建議使用盡量采用現成模板,直接套用即可。但是,由於不知道基本運行原理,容易在遇到問題時束手無策。出於該目的, 筆者將經驗總結,以供參考。
簡單的說來,程序中要使用NML通信,只需要以下簡單的三步。
第一步、RCS安裝
參考文件:https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/rcs-library-installation
以下方法針對的是Ubuntu系統,如果是windows系統或者遇到了安裝困難可以參加附件中的word文檔。
- 確認依賴環境:boost(常用版本有1.54,1.55和1.58)和Java,如果沒有Java,需要按以下步驟安裝java。(其實安裝java的過程即把安裝包解壓到合適的位置,之后建立軟連接)
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 - 下載RCS安裝包/獲取團隊現成安裝包:
http://www.isd.mel.nist.gov/projects/rcslib/rcslib-2014.04.29.tar.gz - 解壓安裝包
- 執行下列語句
cd rcslib-2014.04.29(對應安裝包名字)
./configure -enable-ALLJAVA
[修改Makefile中的prefix選項為 /opt/rcslib/build,此處將生成lib與include]
make
make check
make install(可能需要root權限)
第二步、配置NML文件(configuration file)
以下部分是對NML官方文檔的說明和一些實踐中經驗的補充。不同於官方文檔,我們直接以我們車上的NML(bin/UGVAuto.nml)為模板,來說明。如果想使用,建議直接用模板修改,而不要從頭到尾寫。
NML配置文件主要包括四個部分,分別是:注釋,消息,進程,server。
1.注釋
注釋以'#'開頭,我們可以看到有一行以#開頭,如下:
# Name Type Host size neut RPC# buffer# MP . . .
這就是注釋。
為了更好的表示消息的寫法,特意加了該注釋。
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表示的是消息的使用形式,包括三種方式:SHMEM(share memory),GLOBMEM(global memory),LOCMEM(local memory)(對遠程的Buffer有三種方式:TCP, UDP, RPC)。
- Host表示的是buffer所在的主機名,如果消息只是在同一台機器上傳遞,則這里寫什么都問題不大,最好寫localhost。需要特別注意的是,如果是不同機器傳遞某一個消息,需要給消息寫一個server,來負責傳遞,則這里要寫的是該消息的server所在的主機名(或主機IP地址)。這里可以看到,host1和host4是有通信關系的,很多消息都在這兩台機子中間傳遞。因此, 規划模塊在host1上,與之相關的server也在host1上,因此此處的Host也是寫host1。
- size表示的是消息的大小,單位字節,這里要根據msg定義的數據結構來確認
- 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"。這個與進程的位置無關,表示的是進程如何訪問消息。對於SERVER來說直接訪問,則是用LOCAL。如果用TCP、UDP訪問或者本行的Buffer需與其他主機通信(讀或寫),且server程序不在本機,則是用REMOTE。
- Host表示的是進程所在主機ip地址。
- Ops表示進程對消息的操作方式,R表示讀,W表示寫,RW表示既讀又寫。
- server 對於svr而言為1,否則為0。
- timeout 互斥時間通常寫為0.1。
- master 表示指定消息的傳遞指定哪一個process是master,注意master可以不唯一,且至少需要有一個master,建議如現有文件中所有process對應buffer都為1。如果對於不是master的process,其在c++使用NML,建立new CHANNEL時,會一直等待程序的master建立好后,才能夠正常初始化,否則會一直中斷在此。這個Bug通過調試模式可以發現;或者發現程序沒有報錯,但也並沒有運行至期望的語句時可能出現。
- cnum 除非GLOBMEM,否則沒用。GLOBMEM時表示Process的序列標示,互不相同即可。
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即可。
第三步、程序中調用NML庫函數
這一部分,介紹內容分為兩部分:一是如何使用C++來編寫NMLmessage,二是主程序中如何使用NML來通信。
編寫C++NMLmessage
NML message存放在src文件夾下,通常與應用程序在同一級目錄下。這種目錄結構能夠最小化修改文件目錄。
message文件夾內需要copy以下幾個基本的文件:
-Makefile
-NMLmsgExn2.cc
-NMLmsgExn.hh
-CommonDefinitionX可用可不用,如果不用,則對應將頭文件中的引用注釋即可。
其中,
- Makefile文件,該文件需要針對頭文件、目標文件和NML_CODEGEN的安裝位置,進行修改。通常NML_CODEGEN的安裝位置為:
/usr/local/bin/CodeGenCmdLine.jar
。
如果編譯message時,出現unable to access jarfile,這種情況下需要利用錯誤信息和之前rcd的安裝信息來查找jar文件,find命令參考如下:
find /opt -name CodeGenCmdLine.jar
find /usr -name CodeGenCmdLine.jar
要特別注意makefile文件中,如果定義了make clean -\rm -f *.cc的話最后把其刪除,以免make clean誤刪除一些自定義的cc文件。
clean:
-\rm -f *.o lib*.a lib*.so lib*.la *.lib *.ddll
- CommonDefinitionX和NMLmsgExn.hh不用改,是NML通信使用的模塊。
- 特別注意車上的模板是NMLmsgExn2.cc。此文件同樣不要重命名,不要刪除。
- 自定義message,以“...n.hh”結尾,以表明是用NML通信。
其中,需要引入頭文件包括:
// Prevent Multiple Inclusion
#ifndef MESSAGEAN_HH
#define MESSAGEAN_HH
// Include Files
#include "rcs.hh" // Common RCS definitions
#include "NMLmsgExn.hh"
#include "CommonDefinitionX.hh"
定義MESSAGEA_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
主程序采用NML進行消息傳遞
- NML通信模塊啟用
在主程序中需要引用使用的NMLmessage的頭文件。此外,在啟用NML時使用,使用以下語句:set_rcs_print_destination(RCS_PRINT_TO_STDOUT); nml_start();
nml_cleanup();
- NML通信類的初始化
NML的使用建立在NML通信類的基礎上,初始化NML通信類采用以下命令,但需要已經寫好nml配置文件,關於配置文件的要求,可以參見“配置NML文件(configuration file)”一節:
NML * MESSAGE_CHANNEL = new NML(formatFunction,"buffer", "process", "config.nml");
其中,參數buffer對應配置文件中B的名稱,process對應P的名稱,config.nml對應nml的文件名。
formatFunction一般在nml message文件的最后有同名的定義。
如果配置文件中Process對應的master為1,NML 通信類初始化后,才會啟動buffer的master。 - NML通信消息的初始化
針對寫消息的模塊,采用Message_data = new MESSAGEA_MSG;
Message_data = (MESSAGEC_MSG *) MESSAGE_CHANNEL->get_address();
new MESSAGEA_MSG
。發送的Buffer,需要在構造函數中new MESSAGEA_MSG
開辟新空間。 - 消息讀寫
消息讀采用以下命令判斷接收是否正常,如果正常,可以直接使用buffer中定義變量:MESSAGE_CHANNEL ->blocking_read(0.2)
MESSAGE_CHANNEL ->write(Message_data)
NML-demo
三模塊雙機通信
(written by Bokai Liu)
下載地址:
https://gitee.com/drshawn/RCS-Tutorial/tree/master/HelloWorldModule
該程序在兩台計算機上運行,有三個模塊,分別為(計算機1)模塊A,模塊B和(計算機2)模塊C。
程序運行流程為:
模塊B(發送消息A,B)--->模塊A(接收消息A,B,並進行處理,發送消息C)。
模塊A(發送消息C)--->模塊C(接收消息C)。
NML常見報錯和解決思路
(Welcome to update and supplement)
- 出現如下錯誤
解釋: 這個表示配置文件中缺乏process-line。 - 出現如下錯誤
編譯錯誤MsgAn.cc:-1: error: undefined reference toNMLmsgEx::NMLmsgEx(long, long)
或
解釋: 這表示NMLmsgEx_TYPE無法讀出來。通常是修改了NMLmsgExn2.cc的緣故。(注意腳本中可能出導致該文件被修改。 - 程序不報錯,如果用調試模式,發現程序卡在new NML處。
解釋: 這種錯誤是在nml配置文件中,沒有給buffer指定master的緣故。如果指定了master,則必須要等master開啟后,其它的Process才能夠接收。
附件列表