1、腳本語言
提示
規(guī)則腳本采用Groovy編寫,是一種基于JVM(Java虛擬機)的敏捷開發(fā)語言,Groovy代碼能夠與Java代碼很好的結(jié)合,也能用于擴展現(xiàn)有代碼。
Groovy的語法與 Java 語言的語法很相似,完成同樣的任務(wù)所需的代碼比 Java 代碼更少??梢詫⑺胂癯?Java 語言的一種更加簡單、表達能力更強的變體,基本上可以直接使用Java代碼編寫腳本。
2、新建規(guī)則腳本
規(guī)則腳本列表
編輯規(guī)則腳本
配置說明:
- 腳本事件:設(shè)備上報(已支持)、服務(wù)下發(fā)(已支持)、設(shè)備上線、設(shè)備離線
- 腳本動作:消息重發(fā)(已支持)、消息通知、Http推送、mqtt橋接、數(shù)據(jù)庫存儲
- 所屬產(chǎn)品:規(guī)則腳本跟產(chǎn)品綁定,只會影響產(chǎn)品下面的設(shè)備
- 腳本順序:產(chǎn)品下面有多個腳本,按照順序執(zhí)行
3、規(guī)則腳本基礎(chǔ)示例
重要
如果設(shè)備的Mqtt消息格式是固定的,可以通過規(guī)則引擎進行轉(zhuǎn)發(fā),適配到平臺,系統(tǒng)目前版本只支持Mqtt傳輸協(xié)議的主題和內(nèi)容轉(zhuǎn)發(fā)
- 系統(tǒng)自帶netty-mqtt: 支持主題和內(nèi)容轉(zhuǎn)發(fā)
- 使用emqx中間件: 支持內(nèi)容轉(zhuǎn)發(fā),主題可以通過emqx自帶的規(guī)則引擎轉(zhuǎn)發(fā),emqx規(guī)則引擎介紹
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.core.util.NumberUtil;
// 1. 獲取主題/消息內(nèi)容/設(shè)備編號/協(xié)議編碼
String topic = msgContext.getTopic();
String payload = msgContext.getPayload();
String serialNumber = msgContext.getSerialNumber();
String protocolCode = msgContext.getProtocolCode();
// 2. 數(shù)據(jù)轉(zhuǎn)換(自己處理)
msgContext.logger.info("數(shù)據(jù)轉(zhuǎn)換處理")
String NewTopic = topic;
String NewPayload = payload;
// 3. 返回新的數(shù)據(jù)
msgContext.setTopic(NewTopic);
msgContext.setPayload(NewPayload);
提示
- 默認僅使用hutool的Json處理和數(shù)字工具類庫,hutool其他類庫或者系統(tǒng)相關(guān)類庫,需要后端開放一下;
- 默認禁止使用For/While循環(huán)和IO操作等,根據(jù)情況可以后端開放;
- 通過msgContext獲取和設(shè)置主題和消息內(nèi)容;
- 主題和消息內(nèi)容如何轉(zhuǎn)換需要自己編寫。
規(guī)則腳本通過上下文 MsgContext
存取數(shù)據(jù)。腳本中可以通過 msgContext
獲取值和重新設(shè)置新的值,系統(tǒng)定義的上下文如下:
public class MsgContext {
/** 消息主題 */
private String topic;
/** 消息內(nèi)容 */
private String payload;
/** 設(shè)備編號 */
private String serialNumber;
/** 產(chǎn)品ID */
private Long productId;
/** 協(xié)議編碼 */
private String protocolCode;
}
4. 設(shè)備上報示例
我們創(chuàng)建一個產(chǎn)品,產(chǎn)品選擇JSON協(xié)議

按照平臺處理上報數(shù)據(jù)的系統(tǒng)主題和系統(tǒng)數(shù)據(jù)格式分別是:
Topic: /96/D1ELV3A5TOJS/property/post
[
{
"id": "temperature",
"value": "26.45"
},
{
"id": "humidity",
"value": "65.8"
}
]
實際設(shè)備上報的數(shù)據(jù)格式和主題分別是:
Topic: D1ELV3A5TOJS/post
{
"temperature": 26.5,
"humidity": 65.8
}
此時,我們使用腳本轉(zhuǎn)換主題和數(shù)據(jù)格式,我們新建一個腳本

腳本內(nèi)容如下:
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.core.util.NumberUtil;
//系統(tǒng)主題
String sysTopic = '';
//系統(tǒng)數(shù)據(jù)格式
String sysPayload = '';
// 1. 獲取主題和內(nèi)容,topic S&D1PGLPG58K66/post
String name = msgContext.getTopic();
Long productId = msgContext.getProductId();
String serialNumber = msgContext.getSerialNumber();
String protocolCode = msgContext.getProtocolCode();
String payload = msgContext.getPayload();
msgContext.logger.info("產(chǎn)品id/協(xié)議編號:" + productId + " / " + protocolCode);
// 2. 轉(zhuǎn)換為系統(tǒng)主題 /96/D1ELV3A5TOJS/property/post
sysTopic = "/" + productId + "/" + serialNumber + "/property/post"
if("JSON".equals(protocolCode)){
// 3. 內(nèi)容格式轉(zhuǎn)換
JSONArray newArray = new JSONArray();
JSONObject jsonObject = JSONUtil.parseObj(payload);
jsonObject.keySet().forEach(key -> {
JSONObject newObject =new JSONObject();
newObject.put("id" , key);
newObject.put("value" , jsonObject.getStr(key));
newArray.add(newObject);
});
sysPayload = newArray.toString();
}else{
//其他協(xié)議處理
}
// 4.打印
msgContext.logger.info("新主題:" + sysTopic);
msgContext.logger.info("新內(nèi)容:" + sysPayload);
// 5. 返回新的數(shù)據(jù)(必要)
msgContext.setTopic(sysTopic)
msgContext.setPayload(sysPayload);
腳本內(nèi)容基本比較容易理解。我們編輯好腳本后,進行驗證。運行一下看看效果。連接設(shè)備編號為:S&D1PGLPG58K66 的設(shè)備,按照如下格式發(fā)送:

我們看看腳本執(zhí)行后的在控制臺打印的結(jié)果:
16:08:55.066 [MQTT-BROKER-6-5] INFO c.y.l.c.FlowExecutor - [info,186] - [68bc37d0dffa41a8a25b1d8c1a63a8a5]:requestId has generated
16:08:55.066 [MQTT-BROKER-6-5] INFO c.y.l.c.FlowExecutor - [info,193] - [68bc37d0dffa41a8a25b1d8c1a63a8a5]:slot[10] offered
16:08:55.066 [MQTT-BROKER-6-5] INFO c.y.l.f.element.Node - [info,193] - [68bc37d0dffa41a8a25b1d8c1a63a8a5]:[O]start component[D1751532166174609408(消息轉(zhuǎn)發(fā))] execution
----------------------------------------------------------------------------------------
產(chǎn)品id/協(xié)議編號:41 / JSON
新主題:/41/D1ELV3A5TOJS/property/post
新內(nèi)容:[{"id":"temperature","value":"26.5"},{"id":"humidity","value":"65.8"}]
-----------------------------------------------------------------------------------------
16:08:55.067 [MQTT-BROKER-6-5] INFO c.y.l.c.ScriptCommonComponent - [info,200] - [68bc37d0dffa41a8a25b1d8c1a63a8a5]:component[D1751532166174609408(消息轉(zhuǎn)發(fā))] finished in 0 milliseconds
16:08:55.067 [MQTT-BROKER-6-5] INFO c.y.l.slot.Slot - [info,200] - [68bc37d0dffa41a8a25b1d8c1a63a8a5]:CHAIN_NAME[dataChain]
D1751532166174609408[消息轉(zhuǎn)發(fā)]<0>
可以看到topic和數(shù)據(jù)格式已經(jīng)轉(zhuǎn)換為系統(tǒng)主題和格式
5. 服務(wù)下發(fā)轉(zhuǎn)換示例
假設(shè)實際設(shè)備服務(wù)下發(fā)的主題和數(shù)據(jù)格式分別是:
Topic: D1PGLPG58KZ2/set
{
"report_monitor": "4"
}
平臺系統(tǒng)主題和數(shù)據(jù)格式:
Topic: /96/D1PGLPG58K66/property/post
[
{
"id": "temperature",
"value": "26.45"
},
{
"id": "humidity",
"value": "65.8"
}
]
新增腳本,定義如下:

我們看看腳本的定義內(nèi)容:
1.將系統(tǒng)主題轉(zhuǎn)為真實設(shè)備需要的主題
2.將系統(tǒng)數(shù)據(jù)格式轉(zhuǎn)為真實設(shè)備需要的主題(注意:主題轉(zhuǎn)換時 設(shè)備編號必須帶上且長度大于9位,如果不帶設(shè)備編號,需要修改后端代碼)
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.core.util.NumberUtil;
//系統(tǒng)主題
String tranTopic = '';
//系統(tǒng)數(shù)據(jù)格式
String tranPayload = '';
// 1. 獲取主題和內(nèi)容
String topic = msgContext.getTopic();
String payload = msgContext.getPayload();
Long productId = msgContext.getProductId();
String serialNumber = msgContext.getSerialNumber();
String protocolCode = msgContext.getProtocolCode();
// 2. topic轉(zhuǎn)換 將/60/DEVICE555/function/get 系統(tǒng)主題 轉(zhuǎn)為 DEVICE555/set
tranTopic = serialNumber+ "/set";
if("JSON".equals(protocolCode)){
// 3. 轉(zhuǎn)發(fā)的數(shù)據(jù) {"temperature":26.5,"humidity":65.8}
JSONArray jsonArray = JSONUtil.parseArray(payload);
JSONObject resultObj = new JSONObject();
jsonArray.forEach(obj -> {
JSONObject jsonObject = (JSONObject)obj;
resultObj.put(jsonObject.getStr("id"),jsonObject.getStr("value"));
});
tranPayload = JSONUtil.toJsonStr(resultObj);
}else{
//其他協(xié)議處理
}
// 4.打印
msgContext.logger.info("轉(zhuǎn)發(fā)主題:" + tranTopic);
msgContext.logger.info("轉(zhuǎn)發(fā)內(nèi)容:" + tranPayload);
// 5. 返回新的數(shù)據(jù)(必要)
msgContext.setTopic(tranTopic);
msgContext.setPayload(tranPayload);
我們下發(fā)指令來測試一下轉(zhuǎn)換情況,后端日志內(nèi)容如下:
17:34:36.439 [functionInvokeTask2] INFO c.y.l.c.FlowExecutor - [info,186] - [6481bf113e15499aabe550189d16f4a5]:requestId has generated
17:34:36.440 [functionInvokeTask2] INFO c.y.l.c.FlowExecutor - [info,193] - [6481bf113e15499aabe550189d16f4a5]:slot[13] offered
17:34:36.440 [functionInvokeTask2] INFO c.y.l.f.element.Node - [info,193] - [6481bf113e15499aabe550189d16f4a5]:[O]start component[D1753673875549458432(平臺下發(fā)轉(zhuǎn)發(fā))] execution
-------------------------------------------------
轉(zhuǎn)發(fā)主題:D1PGLPG58KZ2/set
轉(zhuǎn)發(fā)內(nèi)容:{"report_monitor":"6"}
-----------------------------------------------
17:34:36.440 [functionInvokeTask2] INFO c.y.l.c.ScriptCommonComponent - [info,200] - [6481bf113e15499aabe550189d16f4a5]:component[D1753673875549458432(平臺下發(fā)轉(zhuǎn)發(fā))] finished in 0 milliseconds
17:34:36.440 [functionInvokeTask2] INFO c.y.l.slot.Slot - [info,200] - [6481bf113e15499aabe550189d16f4a5]:CHAIN_NAME[dataChain]
客戶端訂閱接收內(nèi)容如下:

至此,將系統(tǒng)主題和數(shù)據(jù)格式轉(zhuǎn)換為設(shè)備所需主題和格式驗證成功。