提示
Modbus設(shè)備可以使用云端輪詢和邊緣網(wǎng)關(guān)兩種方案
一、云端輪詢實現(xiàn)
- 方案實現(xiàn)原因:普通的dtu或模組一般只有實現(xiàn)mqtt或tcp的透傳功能,如果設(shè)備使用modbus協(xié)議,不外加邊緣網(wǎng)關(guān)的情況,modbus協(xié)議的設(shè)備數(shù)據(jù)不會主動上報,因此云端根據(jù)產(chǎn)品對應(yīng)的采集點模板,組裝modbus讀指令下發(fā)到對應(yīng)的設(shè)備,進行modbus輪詢。
- 由于設(shè)備上報modbusRTU報文沒有寄存器標(biāo)記位,需要標(biāo)記報文。
- 目前采用的標(biāo)記方式是加給線程加redis鎖,,等待設(shè)備返回報文。報文如下:

實現(xiàn)方式如下:

Modbus協(xié)議的解碼編碼
1. 平臺代碼
在fastBee平臺中,后端對該報文的解碼編碼在下圖所示位置

2.注解實現(xiàn)解碼編碼
在平臺中 ,封裝了一套以注解方式解碼編碼硬件設(shè)備報文的基礎(chǔ)包,針對各種各樣的硬件設(shè)備報文,都可以使用這種注解方式去實現(xiàn)解碼編碼,
減少后端開發(fā)人員在報文數(shù)據(jù)解碼編碼中繁瑣的問題。

下面看下對應(yīng)封裝了一層的 **“包裝的modbus協(xié)議” ,**使用注解方式如何做到解碼編碼
首先看下注解 @Column的字段如何定義:
這里使用字段
- length 指定類型長度
- version 這里用于區(qū)分是下發(fā)的報文還是上報的報文
- totalUnit 該字段的前置數(shù)量單位 (用于解析批量讀返回的報文)

注解定義解碼編碼modbus協(xié)議
如果你不關(guān)心報文如何解析,可以跳過這里
代碼如下:
int: 使用INT類型表示 1個字節(jié)的報文長度 ,這里INT類型相當(dāng)于byte類型(在Java,INT類型占用字節(jié)為4個字節(jié),這里INT類型會被按照2個字節(jié)進行解析)
totalUnit: 表示該字段的前置數(shù)量單位,例如 totalUnit = 1 ,總數(shù)據(jù)長度為 n,數(shù)據(jù)列表數(shù)為 BYTE[2 * n]
version : 此處用于控制指令類型:
a.當(dāng)有version值時,需要符合條件時該字段才會被解碼編碼 b. 沒有version標(biāo)記時,都參與解碼編碼。 version =0 :表示設(shè)備上報時,該字段才會被解析 version = 1 :表示讀寄存器報文時,該字段才會被解析 version =2 :表示寫單個保持寄存器時, 該字段才會被解析


3. 后端實現(xiàn)云端輪詢
3.1 后端使用定時器方式實現(xiàn)modbus輪詢。
注意:此方式是V2.3及之前版本使用

modbus云端輪詢的后端入口,如下圖所示
篩選在線的網(wǎng)關(guān)設(shè)備,根據(jù)產(chǎn)品定義的采集點,批量下發(fā)讀指令。
使用線程池方式,把每一個子設(shè)備所有要下發(fā)的指令放在同一個子線程下發(fā),使用redis緩存作為線程的鎖也同時作為寄存器的標(biāo)記內(nèi)容,對于每一個子線程對應(yīng)一個子設(shè)備,一個子設(shè)備對應(yīng)多條需要批量讀的下發(fā)指令,當(dāng)下發(fā)指令時,子線程會不斷加鎖輪詢,直到超過redis緩存設(shè)定的緩存時間,或者收到設(shè)備上報的數(shù)據(jù)時,才會繼續(xù)下發(fā)該子設(shè)備的下一條指令。具體請參考后端代碼。

3.2 產(chǎn)品側(cè)Modbus配置采集方式為云端輪詢。
注意:此方式是V2.4及之后版本使用


二、邊緣網(wǎng)關(guān)
提示
邊緣網(wǎng)關(guān)配合云端采集(實時)
1. 方案介紹
由于modbus協(xié)議的特性,設(shè)備上報到云平臺的報文,缺少可識別的寄存器地址值,所以平臺識別不了寄存器地址所對應(yīng)的物模型采集點,因此為了提高采集效率,和采集數(shù)據(jù)的實時性,再考慮到通用性問題,這里方案考慮使用外加MCU芯片的做法,實現(xiàn)邊緣網(wǎng)關(guān)。
實現(xiàn)硬件:
- 用戶自身的設(shè)備(MCU)
- dtu或模組 (透傳)
- 外加單片機芯片 (modbus輪詢,變化上報 (成本低))
dtu或者模組可以
2. 報文數(shù)據(jù)
為了解決modbus原始報文寄存器地址的問題,外加MCU模塊會對modbus協(xié)議再封裝一層,設(shè)備數(shù)據(jù)組成:
- a. 設(shè)備主動上報數(shù)據(jù)組成:
FFAA : 外加的報文頭,保證消息完整
0D : 外加報文尾,保證消息完整
0001:寄存器地址,用于標(biāo)識消息
010302030578B7 : 原始modbus報文

- b. 服務(wù)下發(fā)數(shù)據(jù)組成

- c. 設(shè)備應(yīng)答服務(wù)下發(fā)數(shù)據(jù)組成
modbus協(xié)議設(shè)備在收到寫指令執(zhí)行完成時,會將下發(fā)的寫指令主動上報給平臺表示已經(jīng)執(zhí)行成功。

3. 總體流程圖如下:

4. 平臺代碼
在fastBee平臺中,后端對該報文的解碼編碼在下圖所示位置

注解實現(xiàn)解碼編碼
在平臺中 ,封裝了一套以注解方式解碼編碼硬件設(shè)備報文的基礎(chǔ)包,針對各種各樣的硬件設(shè)備報文,都可以使用這種注解方式去實現(xiàn)解碼編碼,
減少后端開發(fā)人員在報文數(shù)據(jù)解碼編碼中繁瑣的問題。

下面看下對應(yīng)封裝了一層的 **“包裝的modbus協(xié)議” ,**使用注解方式如何做到解碼編碼
首先看下注解 @Column的字段如何定義:
這里使用字段
- length 指定類型長度
- version 這里用于區(qū)分是下發(fā)的報文還是上報的報文
- totalUnit 該字段的前置數(shù)量單位 (用于解析批量讀返回的報文)

下面看下這個報文的定義
在java中,所有的基礎(chǔ)數(shù)據(jù)類型是不存在有符號無符號,
int: 使用INT類型表示 1個字節(jié)的報文長度 ,這里INT類型相當(dāng)于byte類型(在Java,INT類型占用字節(jié)為4個字節(jié),這里INT類型會被按照2個字節(jié)進行解析)
在下面定義中 FFDD 是兩個字節(jié),length=2 ,用int類型定義表示一個字節(jié),那么這個字段的解析長度為: 2個字節(jié)
short:表示兩個字節(jié),modbus數(shù)據(jù)位的組成是2個字節(jié),用 short[] 表示是short數(shù)組,這里兼容了單個和批量解析報文里面的數(shù)據(jù)位。
最終解析出來的每個數(shù)據(jù)存放在 data數(shù)組中
totalUnit: 表示該字段的前置數(shù)量單位,例如 totalUnit = 1 ,總數(shù)據(jù)長度為 n,數(shù)據(jù)列表數(shù)為 BYTE[2 * n]

注解方式解碼編碼測試
后端代碼測試類為**:PakModbusTest **,如下圖所示。

結(jié)果如下:
單個上報解析=讀保持寄存器(字節(jié)讀寫模式)[,slaveId[從機地址]=1,code[功能碼]=3,length[返回數(shù)據(jù)個數(shù)]1,data[上報數(shù)據(jù)]=[773],address[寄存器地址]=1,writeData[下發(fā)數(shù)據(jù)]=0]
批量上報報文解析=讀保持寄存器(字節(jié)讀寫模式)[,slaveId[從機地址]=1,code[功能碼]=3,length[返回數(shù)據(jù)個數(shù)]80,data[上報數(shù)據(jù)]=[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16384, 0, 0, 0, 3327, 924, 32, 0, -1, -1, 0, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 0, -55, 16, 25, 30, 16, 25, 25, 87, 7],address[寄存器地址]=1,writeData[下發(fā)數(shù)據(jù)]=0]