楊國(guó)棟+高云嶺
摘 要:針對(duì)Modbus網(wǎng)關(guān)大部分產(chǎn)品為硬件實(shí)現(xiàn)成本高,可擴(kuò)展性差且功能單一的問(wèn)題,提出了一種在基于軟件實(shí)現(xiàn)Modbus網(wǎng)關(guān)的方法。該方法支持異種網(wǎng)絡(luò)設(shè)備拓?fù)浼軜?gòu),支持多進(jìn)程處理進(jìn)一步提高M(jìn)odbus協(xié)議的吞吐量。同時(shí)在嵌入式Linux和Arm硬件平臺(tái)架構(gòu)上對(duì)該方法的正確性進(jìn)行了檢測(cè)驗(yàn)證。
關(guān)鍵詞:Modbus;軟件網(wǎng)關(guān);異種網(wǎng)絡(luò);吞吐量
中圖分類號(hào):TP273 ? ?文獻(xiàn)標(biāo)識(shí)碼:A ? ? 文章編號(hào):2095-1302(2015)01-00-04
0 ?引 ?言
Modbus協(xié)議作為一種在工業(yè)控制領(lǐng)域廣泛使用的總線協(xié)議,其有著標(biāo)準(zhǔn)、開放,支持多種電氣接口,以及幀格式簡(jiǎn)單、緊湊、通俗易懂等優(yōu)點(diǎn)。據(jù)不完全統(tǒng)計(jì),自Modbus協(xié)議1979年面世以來(lái),截止2007年,其已被應(yīng)用在了超過(guò)1 000萬(wàn)個(gè)設(shè)備節(jié)點(diǎn)中。
由于Modbus協(xié)議本身支持TCP和RTU兩種鏈路連接方式。對(duì)于TCP,其基于以太網(wǎng)為物理連接鏈路。而對(duì)于RTU,一般基于RS 232或者RS 485作為物理鏈路連接。對(duì)于Modbus設(shè)備節(jié)點(diǎn)的組網(wǎng),通常會(huì)基于同種物理鏈路,基于TCP方式的組網(wǎng),可以使用以太網(wǎng)交換機(jī)來(lái)完成。而對(duì)于RTU方式的組網(wǎng),RS 485通過(guò)雙絞線直接串接在一起即可。
但由于實(shí)際應(yīng)用場(chǎng)景中,通常會(huì)結(jié)合兩種網(wǎng)絡(luò)的優(yōu)點(diǎn)。如以太網(wǎng)傳輸速度快,具有高吞吐率的特點(diǎn),RS 485具有組網(wǎng)簡(jiǎn)單,傳輸距離長(zhǎng)的特點(diǎn)。通常采用Modbus硬件網(wǎng)關(guān)來(lái)結(jié)合兩種網(wǎng)絡(luò)組成異種網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)。但硬件網(wǎng)關(guān)具有接口數(shù)目固定,成本高,不便擴(kuò)展等缺點(diǎn)。
本文針對(duì)這些實(shí)際應(yīng)用和產(chǎn)品維護(hù)期間遇到的問(wèn)題,設(shè)計(jì)實(shí)現(xiàn)了一種針對(duì)異種網(wǎng)絡(luò)的,設(shè)置靈活,性能可靠,易擴(kuò)展,低成本的軟件網(wǎng)關(guān)實(shí)現(xiàn)方法。
1 ?總體架構(gòu)設(shè)計(jì)
Modbus協(xié)議通常用在如油田、車間等有多設(shè)備需要進(jìn)行組網(wǎng)且有復(fù)雜工況的環(huán)境下。多臺(tái)上位機(jī)可以通過(guò)RJ45連接到以太網(wǎng)交換機(jī),其通過(guò)1502端口發(fā)送標(biāo)準(zhǔn)的TCP數(shù)據(jù)包到以太網(wǎng)交換機(jī)。運(yùn)行有Modbus網(wǎng)關(guān)的Modbus Server連接到以太網(wǎng)交換機(jī),其接收Modbus TCP數(shù)據(jù)請(qǐng)求并分析目的地址后將該數(shù)據(jù)請(qǐng)求分發(fā)到對(duì)應(yīng)的Modbus Server中。數(shù)據(jù)分發(fā)時(shí),組件對(duì)應(yīng)的RS 485數(shù)據(jù)包并通過(guò)485總線傳遞數(shù)據(jù)。具體結(jié)構(gòu)如圖1所示。
圖1 ?Modbus異種網(wǎng)絡(luò)組網(wǎng)拓?fù)鋱D
一種在實(shí)際生產(chǎn)環(huán)境中經(jīng)常會(huì)見到的用例是:在油田監(jiān)控網(wǎng)絡(luò)中,上位機(jī)運(yùn)行于監(jiān)控室監(jiān)控各個(gè)油井抽油機(jī)的狀態(tài)。而Modbus Server作為安裝在每一個(gè)油井抽油機(jī)上的監(jiān)控裝置用向采集油井狀態(tài)并向上位機(jī)匯報(bào)。運(yùn)行有Modbus網(wǎng)關(guān)的Modbus Sever則可作為井場(chǎng)主監(jiān)控裝置起到一個(gè)數(shù)據(jù)轉(zhuǎn)發(fā)的作用。由于油井之間的距離長(zhǎng)度通常會(huì)超過(guò)100 m,因此采用485串行物理鏈路的方式會(huì)更便于油井采集器之間的組網(wǎng)。而各個(gè)油井設(shè)備的數(shù)據(jù)匯聚到了主采集器之后,其過(guò)大的數(shù)據(jù)量對(duì)于485串行鏈路而言負(fù)擔(dān)較重,容易丟失數(shù)據(jù)。因此主采集器采用以太網(wǎng)物理鏈路與上位機(jī)連接。
1.1 ?網(wǎng)關(guān)結(jié)構(gòu)
Modbus網(wǎng)關(guān)運(yùn)行于Modbus Server中,可根據(jù)配置文件來(lái)配置為是否啟動(dòng)網(wǎng)關(guān)。啟動(dòng)了網(wǎng)關(guān)的Modbus Server本身和其它Modbus Server設(shè)備一樣,也可以提供數(shù)據(jù)采集功能。
為了能接收多個(gè)上位機(jī)的數(shù)據(jù)請(qǐng)求,運(yùn)行了Modbus網(wǎng)關(guān)的Modbus Server會(huì)啟動(dòng)一個(gè)支持多路輸入的Socket服務(wù)器用于監(jiān)聽上位機(jī)的TCP數(shù)據(jù)請(qǐng)求。對(duì)于請(qǐng)求本機(jī)地址的數(shù)據(jù),將直接返回相關(guān)數(shù)據(jù)。而對(duì)于其它Modbus Server地址的數(shù)據(jù)請(qǐng)求,將被網(wǎng)關(guān)中的數(shù)據(jù)分發(fā)器Dispatcher通過(guò)啟動(dòng)一個(gè)獨(dú)立進(jìn)程的方式分發(fā)到Sub-Modbus Server中。為保證數(shù)據(jù)多個(gè)數(shù)據(jù)請(qǐng)求之間不會(huì)在485總線網(wǎng)絡(luò)中造成沖突,在數(shù)據(jù)分發(fā)器啟動(dòng)的多個(gè)數(shù)據(jù)處理進(jìn)程和Sub-Modbus Servers之間,會(huì)存在一個(gè)總線鎖。Modbus網(wǎng)關(guān)結(jié)構(gòu)如圖2所示。
圖2 ?Modbus網(wǎng)關(guān)結(jié)構(gòu)
1.2 ?目的地址傳遞
Modbus協(xié)議定義了一個(gè)與基礎(chǔ)通信層無(wú)關(guān)的簡(jiǎn)單協(xié)議數(shù)據(jù)單元(PDU)。特定總線或者網(wǎng)絡(luò)上的Modbus協(xié)議映射能夠在應(yīng)用數(shù)據(jù)單元(ADU)上引入一些附加域,如地址域或差錯(cuò)校驗(yàn)域。Modbus通用數(shù)據(jù)幀如圖三所示。
圖3 ?Modbus通用數(shù)據(jù)幀
一個(gè)ADU最大長(zhǎng)度為256 B。對(duì)于TCP通信鏈路,通常其ADU不包含差錯(cuò)校驗(yàn)部分,而會(huì)包含一個(gè)7 B的Modbus報(bào)文頭(MBAP)。MBAP的組成為2 B的事務(wù)元標(biāo)識(shí)符,2 B的協(xié)議標(biāo)識(shí)符以及2 B的數(shù)據(jù)包長(zhǎng)度和1 B的單元標(biāo)識(shí)符。通常情況下,Modbus網(wǎng)關(guān)中在向串行鏈路設(shè)備轉(zhuǎn)發(fā)數(shù)據(jù)時(shí)的設(shè)備地址就保存在單元標(biāo)識(shí)符中。TCP通信鏈路下,ADU組成為:
TCP MODBUS ADU = 249 B+ MBAP (7 B) = 256 B
對(duì)于串型鏈路通信來(lái)說(shuō),其地址域?yàn)橐粋€(gè)字節(jié)長(zhǎng)度。而差錯(cuò)校驗(yàn)部分存儲(chǔ)的數(shù)據(jù)通過(guò)CRC16算法所得,其數(shù)據(jù)長(zhǎng)度為2 B。因此最大PDU長(zhǎng)度為253 B。一個(gè)最大長(zhǎng)度的ADU組成為:
RS 232 / RS 485 ADU = 253 B + 服務(wù)器地址(1 B) + CRC (2 B) = 256 B。
1.3 ?數(shù)據(jù)包解析
Modbus定義了4種具有不同特征的數(shù)據(jù)模型,分別是:
(1)離散量輸入,為只讀的單個(gè)比特位;
(2)線圈變量,為可讀寫的單個(gè)比特位;
(3)輸入寄存器,為只讀的2 B;
(4)輸出寄存器,為可讀寫的2 B。
對(duì)于這4種數(shù)據(jù),Modbus協(xié)議都允許單個(gè)選擇65 536個(gè)數(shù)據(jù)項(xiàng),但實(shí)際而言,數(shù)據(jù)的大小規(guī)格限制和事務(wù)處理的功能碼是相關(guān)聯(lián)的。在本實(shí)現(xiàn)中,取工業(yè)控制領(lǐng)域常用的8個(gè)功能碼來(lái)作為數(shù)據(jù)網(wǎng)關(guān)中處理的數(shù)據(jù)請(qǐng)求,如下:
0x01 讀線圈
0x02 讀輸入離散量
0x03 讀多個(gè)輸出寄存器
0x04 讀多個(gè)輸入寄存器
0x05 寫單個(gè)線圈
0x06 寫單個(gè)寄存器
0x0F 寫多個(gè)線圈
0x10寫多個(gè)寄存器
以讀多個(gè)輸出寄存器為例。讀數(shù)據(jù)時(shí),請(qǐng)求PDU中需指定輸出寄存器的起始地址和讀取數(shù)量。一個(gè)讀取輸出寄存器第108到110的3個(gè)寄存器的數(shù)據(jù)如表1所示。
表1 ?讀輸出寄存器
請(qǐng)求 響應(yīng)
名稱 數(shù)值(十
六進(jìn)制) 名稱 數(shù)值(十
六進(jìn)制)
功能碼 03 功能碼 03
起始地址(高) 00 數(shù)據(jù)長(zhǎng)度 06
起始地址(低) 6B 寄存器值(高)- 108 02
讀取寄存器個(gè)(高) 00 寄存器值(低)- 108 2B
讀取寄存器個(gè)(低) 03 寄存器值(高)- 109 00
寄存器值(低)- 109 00
寄存器值(高)- 110 00
寄存器值(低)- 110 64
在網(wǎng)關(guān)中對(duì)數(shù)據(jù)解析時(shí),對(duì)于不同的功能碼,應(yīng)根據(jù)Modbus協(xié)議中的數(shù)據(jù)格式定義來(lái)對(duì)數(shù)據(jù)包進(jìn)行解析。
2 ?模塊功能詳細(xì)設(shè)計(jì)
2.1 ?Socket服務(wù)器模塊設(shè)計(jì)
軟件網(wǎng)關(guān)與上位機(jī)之間通過(guò)TCP進(jìn)行連接,網(wǎng)關(guān)將啟動(dòng)一個(gè)socket服務(wù)器監(jiān)聽來(lái)自上位機(jī)的Modbus客戶端的請(qǐng)求。由于會(huì)存在多臺(tái)上位機(jī),socket服務(wù)器在設(shè)計(jì)中需考慮可同時(shí)接受多個(gè)TCP請(qǐng)求。而對(duì)于TCP請(qǐng)求,在一條鏈路已經(jīng)建立以后,來(lái)自同一臺(tái)上位機(jī)的再次請(qǐng)求應(yīng)不需要再次進(jìn)行鏈路的建立。
首先對(duì)于一個(gè)未建立過(guò)連接的TCP請(qǐng)求,需要為該請(qǐng)求創(chuàng)建必要的工作環(huán)境并保存環(huán)境??紤]到網(wǎng)關(guān)的處理能力,對(duì)于允許的最大連接數(shù)將通過(guò)配置文件進(jìn)行讀取。
// Clear the reference set of socket
FD_ZERO(&refset);
// Add the server socket
FD_SET(server_socket, &refset);
// Keep track of the max file descriptor
fdmax = server_socket;
for (;;) {
rdset = refset;
select(fdmax+1, &rdset, NULL, NULL, NULL);
// Run through the existing connections looking for data to be read
for (master_socket = 0; master_socket <= fdmax; master_socket++) {
if (FD_ISSET(master_socket, &rdset)) {
if (master_socket == server_socket) {
/* A client is asking a new connection */
memset(&clientaddr, 0, sizeof(clientaddr));
newfd = accept(server_socket, (struct sockaddr *)&clientaddr, &addrlen);
if (newfd == -1) { perror(“Server accept() error”); ?}
else { FD_SET(newfd, &refset);
if (newfd > fdmax) {
fdmax = newfd; ?/* Keep track of the maximum */
} ? } ? }
}
}
}
而對(duì)于一個(gè)已經(jīng)建立好的連接,則需要調(diào)用轉(zhuǎn)發(fā)函數(shù)將其轉(zhuǎn)發(fā)到對(duì)應(yīng)的子設(shè)備中去。對(duì)于目的地址為網(wǎng)關(guān)設(shè)備本身的,需要根據(jù)上位機(jī)的請(qǐng)求,將網(wǎng)關(guān)設(shè)備本身采集到的數(shù)據(jù)返回給上位機(jī)或者完成對(duì)應(yīng)的寫數(shù)據(jù)操作。在對(duì)于網(wǎng)關(guān)設(shè)備本身的操作中,由于modbus數(shù)據(jù)通常為由一個(gè)獨(dú)立的進(jìn)程采集并存放到共享內(nèi)存中,因此在操作共享內(nèi)存時(shí),需使用信號(hào)量來(lái)保證數(shù)據(jù)的正確性。
/* An already connected master has sent a new query */
modbus_set_socket(ctx, master_socket);
rc = modbus_receive(ctx, query);
if (rc > 0) {if (query[header_length -1] != config->server_id) {
device_write_hook (query, header_length, config, (share_mm_data *)share_mm);
sem_wait(&sem);
copy_mem_from_sharing (share_mm, mb_mapping); ?// copy memory form sharing mem into modbus mapping area.
sem_post(&sem);
} else {modbus_dispatch (ctx, query, config); } ?// For gateway mode switch
modbus_reply(ctx, query, rc, mb_mapping);}
2.2 ?組包模塊設(shè)計(jì)
在組建Modbus數(shù)據(jù)包時(shí), 由于不同的Modbus請(qǐng)求命令其數(shù)據(jù)包組成結(jié)構(gòu)不相同,因此需根據(jù)Modbus請(qǐng)求命令的不同來(lái)進(jìn)行判斷。
組包模塊主要提供2個(gè)函數(shù),其定義如下:
int package_read_data (char *data_485, char *data_tcp, int direction);
int package_write_data (char *data_485, char *data_tcp, int direction);
package_read_data函數(shù)用于將Modbus的讀數(shù)據(jù)請(qǐng)求進(jìn)行TCP數(shù)據(jù)包和485數(shù)據(jù)包之間的轉(zhuǎn)換,根據(jù)direction參數(shù)的變化來(lái)決定轉(zhuǎn)換的方向。而package_write_data函數(shù)則用于將Modbus的寫數(shù)據(jù)請(qǐng)求進(jìn)行TCP數(shù)據(jù)包和485數(shù)據(jù)包之間的轉(zhuǎn)換。
對(duì)于讀數(shù)據(jù),收到的數(shù)據(jù)包中相關(guān)的Modbus功能碼為0x01、0x02、0x03、0x04,其數(shù)據(jù)復(fù)制組包的過(guò)程如圖4所示:
圖4 ?讀數(shù)據(jù)時(shí)數(shù)據(jù)復(fù)制包過(guò)程
對(duì)于寫數(shù)據(jù),收到的數(shù)據(jù)包中相關(guān)的Modbus功能碼為0x05、0x06、0x0F、0x10,其數(shù)據(jù)復(fù)制組包的過(guò)程如圖5所示:
圖5 ?寫數(shù)據(jù)時(shí)數(shù)據(jù)復(fù)制包過(guò)程
2.3 ?轉(zhuǎn)發(fā)模塊設(shè)計(jì)
數(shù)據(jù)轉(zhuǎn)發(fā)模塊首先會(huì)啟動(dòng)一個(gè)獨(dú)立的進(jìn)程并在該進(jìn)程中完成數(shù)據(jù)轉(zhuǎn)發(fā)的全部過(guò)程。由于數(shù)據(jù)轉(zhuǎn)發(fā)的過(guò)程中,可能會(huì)有其它上位機(jī)的數(shù)據(jù)請(qǐng)求再次到來(lái)并啟動(dòng)一個(gè)進(jìn)程通過(guò)485總線向下位機(jī)傳遞數(shù)據(jù),因此在任意一個(gè)進(jìn)程通過(guò)485總線傳輸數(shù)據(jù)時(shí),需對(duì)該總線進(jìn)行上鎖。同2.1節(jié)中設(shè)計(jì)一樣,此處采用信號(hào)量完成該工作。
網(wǎng)關(guān)運(yùn)行過(guò)程中,由于父進(jìn)程可能會(huì)隨時(shí)退出,在數(shù)據(jù)轉(zhuǎn)發(fā)子進(jìn)程中需監(jiān)控父進(jìn)程信息。如父進(jìn)程退出,則數(shù)據(jù)轉(zhuǎn)發(fā)子進(jìn)程也應(yīng)退出從而避免成為僵尸進(jìn)程。核心代碼如下:
pid=fork();
if (pid == -1) { exit(1); ?}
else if (pid == 0) {
// Child
share_mm= shmat (shmid, 0, 0);
ctx = modbus_new_rtu(config->dev, config->baudrate, config->parity, config->data_bit, config->stop_bit);
package_485_data (query, output);
sem_wait(&sem_485);
send_485_data (output);
sem_post(&sem_485);
while (1) { ? ?// 判斷父進(jìn)程是否已退出
if (getppid() == 1) { exit(0); ?}
// Wait response
modbus_receive_confirmation(ctx, response);
if (response != -1) { ?break; ?}
}
package_tcp_data (response, output);
send_tcp_data (output);
}
其流程如圖6所示:
圖6 ?數(shù)據(jù)轉(zhuǎn)發(fā)流程
3 ?功能驗(yàn)證
以AT91RM9200為硬件平臺(tái),以Linux 3.8為內(nèi)核以及基于Busybox 1.2.0組件的根文件系統(tǒng)為軟件平臺(tái)作為Modbus服務(wù)器的測(cè)試平臺(tái)。上位機(jī)為普通PC,運(yùn)行Ubutntu 12.04?;诒疚闹袌D一搭建測(cè)試網(wǎng)絡(luò)結(jié)構(gòu)。驗(yàn)證的主要過(guò)程如下:
3.1 ?客戶端配置
(1)配置服務(wù)器IP及Modbus網(wǎng)關(guān)IP。假設(shè)服務(wù)器端IP地址信息為192.168.1.110,運(yùn)行網(wǎng)關(guān)的Modbus網(wǎng)關(guān)服務(wù)器的IP地址信息為:192.168.1.111。需更改Modbus網(wǎng)關(guān)服務(wù)器應(yīng)用程序配置文件/etc/modbus/config.ini文件如下:
[GLOBAL]
type=route
mode=tcp
server_id=1
[NETWORK]
ip_addr=192.168.1.111
(2)將子Modbus服務(wù)器通過(guò)485線纜連接到Modbus網(wǎng)關(guān)服務(wù)器。
(3)啟動(dòng)Modbus網(wǎng)關(guān)應(yīng)用程序。
(4)各子Modbus服務(wù)器啟動(dòng)Modbus服務(wù)應(yīng)用。
3.2 ?服務(wù)器端配置
服務(wù)器端運(yùn)行一個(gè)封裝了標(biāo)準(zhǔn)Modbus請(qǐng)求的客戶端應(yīng)用程序即可。通過(guò)TCP方式向Modbus網(wǎng)關(guān)服務(wù)器發(fā)送Modbus數(shù)據(jù)請(qǐng)求。
3.3 ?驗(yàn)證結(jié)果
在Ubuntu 12.04 上位機(jī)開始執(zhí)行Modbus,以TCP方式向Modbus網(wǎng)關(guān)服務(wù)器分別發(fā)送讀、寫數(shù)據(jù)請(qǐng)求。測(cè)試Modbus網(wǎng)關(guān)收到了信息并進(jìn)行了數(shù)據(jù)轉(zhuǎn)發(fā),而對(duì)應(yīng)的子Modbus服務(wù)器也收到了從Modbus網(wǎng)關(guān)服務(wù)器發(fā)送的485數(shù)據(jù)格式的數(shù)據(jù)請(qǐng)求。測(cè)試結(jié)果如圖7所示。
圖7 ?調(diào)試信息采集和集成測(cè)試
測(cè)試重點(diǎn)完成了兩項(xiàng)驗(yàn)證,其一,上位機(jī)發(fā)送的數(shù)據(jù)被Modbus網(wǎng)關(guān)所收到并進(jìn)行了轉(zhuǎn)發(fā);其二,子Modbus服務(wù)器收到了Modbus網(wǎng)關(guān)的數(shù)據(jù)請(qǐng)求。
4 ?結(jié) ?語(yǔ)
本文主要描述了一種異種網(wǎng)絡(luò)Modbus軟件網(wǎng)關(guān)的設(shè)計(jì)與實(shí)現(xiàn),考慮了多種實(shí)際工作中的應(yīng)用場(chǎng)景。結(jié)合了485物理鏈路和以太網(wǎng)物理鏈路的優(yōu)點(diǎn),可以應(yīng)用在多任務(wù)、多設(shè)備的復(fù)雜網(wǎng)絡(luò)拓?fù)渲?。同比較目前常見的硬件網(wǎng)關(guān)而言,其低成本、易擴(kuò)展的特點(diǎn)具有非?,F(xiàn)實(shí)的積極意義。而在油田中部署的油井監(jiān)控系統(tǒng)也充分驗(yàn)證了本系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)。
參考文獻(xiàn)
[1]李英奇,吳桂初.Modbus-ModbusTCP/IP的網(wǎng)關(guān)設(shè)計(jì)[J]. 微型機(jī)與應(yīng)用, 2013(10):48-50.
[2]翁建年,張浩,彭道剛,等. 基于嵌入式ARM的Modbus/TCP協(xié)議的研究與實(shí)現(xiàn)[J].計(jì)算機(jī)應(yīng)用與軟件,2009,26(10):36-39.
[3] Wikipedia.Modbus[EB/OL]. http://en.wikipedia.org/wiki/Modbus,2013.