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

實現方式如下:

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

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

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

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


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

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

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


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

- b. 服務下發(fā)數據組成

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

3. 總體流程圖如下:

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

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

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

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

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

結果如下:
單個上報解析=讀保持寄存器(字節(jié)讀寫模式)[,slaveId[從機地址]=1,code[功能碼]=3,length[返回數據個數]1,data[上報數據]=[773],address[寄存器地址]=1,writeData[下發(fā)數據]=0]
批量上報報文解析=讀保持寄存器(字節(jié)讀寫模式)[,slaveId[從機地址]=1,code[功能碼]=3,length[返回數據個數]80,data[上報數據]=[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ā)數據]=0]