NML工程入門-三步使用




NML工程入門-三步使用




撰寫人:李峻翔
特別鳴謝:劉伯凱
版本:V1.0


TOC

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文檔。

  1. 確認依賴環境: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
  2. 下載RCS安裝包/獲取團隊現成安裝包:
    http://www.isd.mel.nist.gov/projects/rcslib/rcslib-2014.04.29.tar.gz
  3. 解壓安裝包
  4. 執行下列語句
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進行消息傳遞

  1. NML通信模塊啟用
    在主程序中需要引用使用的NMLmessage的頭文件。此外,在啟用NML時使用,使用以下語句:
    set_rcs_print_destination(RCS_PRINT_TO_STDOUT);
    nml_start();
    在程序通信結束后,需要清空和關閉NML:
    nml_cleanup();
  2. 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。
  3. NML通信消息的初始化
    針對寫消息的模塊,采用
    Message_data = new MESSAGEA_MSG;
    針對讀消息的模塊,采用
    Message_data = (MESSAGEC_MSG *) MESSAGE_CHANNEL->get_address();
    需要注意的是,接收的buffer,不需要重新new MESSAGEA_MSG。發送的Buffer,需要在構造函數中new MESSAGEA_MSG開辟新空間。
  4. 消息讀寫
    消息讀采用以下命令判斷接收是否正常,如果正常,可以直接使用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 to NMLmsgEx::NMLmsgEx(long, long)

    解釋: 這表示NMLmsgEx_TYPE無法讀出來。通常是修改了NMLmsgExn2.cc的緣故。(注意腳本中可能出導致該文件被修改。
  • 程序不報錯,如果用調試模式,發現程序卡在new NML處。
    解釋: 這種錯誤是在nml配置文件中,沒有給buffer指定master的緣故。如果指定了master,則必須要等master開啟后,其它的Process才能夠接收。

參考文獻:
https://www.nist.gov/el/intelligent-systems-division-73500/networked-control-systems-group/nml-programmers-guide-c

附件列表

     


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM