設備消息協議解析SDK
平臺封裝了網絡通信,但是具體的數據由消息協議進行解析.協議(ProtocolSupport)
主要由認證器(Authenticator)
,
消息編解碼器(DeviceMessageCodec)
,消息發送攔截器(DeviceMessageSenderInterceptor)
以及配置元數據(ConfigMetadata)
組成.
認證器
認證器(Authenticator)是用於在收到設備請求(例如MQTT)時,對客戶端進行認證時使用,不同的網絡協議(Transport)使用不同的認證器.
接口定義:
public interface Authenticator {
Mono<AuthenticationResponse> authenticate(@Nonnull AuthenticationRequest request,
@Nonnull DeviceOperator device);
}
參數AuthenticationRequest
爲認證請求參數,不同的網絡類型請求類型也不同,請根據實際情況轉換爲對應的類型,例如:
MqttAuthenticationRequest mqttRequest = (MqttAuthenticationRequest)request;
參數DeviceOperator
爲對應的設備操作接口,可通過此接口獲取設備的配置,例如:device.getConfig("mqttUsername")
.
返回值Mono<AuthenticationResponse>
爲認證結果.
例:
Authenticator mqttAuthenticator = (request, device) -> {
MqttAuthenticationRequest mqttRequest = ((MqttAuthenticationRequest) request);
return device.getConfigs("username", "password") //獲取設備的配置信息,由配置元數據定義,在設備型號中進行配置.
.flatMap(values -> {
String username = values.getValue("username").map(Value::asString).orElse(null);
String password = values.getValue("password").map(Value::asString).orElse(null);
if (mqttRequest.getUsername().equals(username) && mqttRequest.getPassword().equals(password)) {
return Mono.just(AuthenticationResponse.success());
} else {
return Mono.just(AuthenticationResponse.error(400, "密碼錯誤"));
}
});
}
消息編解碼器
用於將平臺統一的消息(Message)
與設備端能處理的消息(EncodedMessage)
進行相互轉換.
接口(DeviceMessageCodec
)定義:
//此編解碼器支持的網絡協議,如: DefaultTransport.MQTT
Transport getSupportTransport();
//將平臺發往設備的消息編碼爲設備端對消息
Publisher<? extends EncodedMessage> encode(MessageEncodeContext context);
//將設備發往平臺的消息解碼爲平臺統一的消息
Publisher<? extends Message> decode(MessageDecodeContext context);
編碼: 可以從上下文MessageEncodeContext
中獲取當前設備操作接口DeviceOperator
以及平臺統一的設備消息Message
.根據設備側定義的協議轉換爲對應的EncodedMessage
.
tip 注意: 不同的網絡協議需要轉換爲不同的
EncodedMessage
類型.比如,MQTT需要轉換爲MqttMessage
.
大部分情況下:MessageDecodeContext
可轉爲FromDeviceMessageContext
,可獲取到當前設備的連接會話DeviceSession
,通過會話可以直接發送消息到設備.
解碼: 可以從上下文MessageDecodeContext
中獲取設備操作接口DeviceOperator
以及設備消息EncodedMessage
,然後將消息轉換爲平臺統一的消息.
平臺統一消息定義
平臺將設備抽象爲由屬性(property)
,功能(function)
,事件(event)
組成.
平臺接入設備之前,應該先將設備的屬性``功能``事件
設計好.
消息組成
消息主要由deviceId
,messageId
,headers
組成.
deviceId
爲設備的唯一標識,messageId
爲消息的唯一標識,headers
爲消息頭,通常用於對自定義消息處理的行爲,如是否異步消息,
是否分片消息等.
常用的(Headers)[https://github.com/jetlinks/jetlinks-core/blob/master/src/main/java/org/jetlinks/core/message/Headers.java]:
- aysnc 是否異步,boolean類型.
- timeout 指定超時時間. 毫秒.
- frag_msg_id 分片主消息ID,爲下發消息的
messageId
- frag_num 分片總數
- frag_part 當前分片索引
- frag_last 是否爲最後一個分片,當無法確定分片數量的時候,可以將分片設置到足夠大,最後一個分片設置:
frag_last=true
來完成返回. - keepOnline 與
DeviceOnlineMessage
配合使用,在TCP短鏈接,保持設備一直在線狀態,連接斷開不會設置設備離線.
tip:messageId通常由平臺自動生成,如果設備不支持消息id,可在自定義協議中通過Map的方式來做映射,將設備返回的消息與平臺的messageId進行綁定.
屬性相關消息
獲取設備屬性(ReadPropertyMessage)
對應設備回覆的消息ReadPropertyMessageReply
.修改設備屬性(WritePropertyMessage)
對應設備回覆的消息WritePropertyMessageReply
.設備上報屬性(ReportPropertyMessage)
由設備上報.
注意:設備回覆的消息是通過messageId進行綁定,messageId應該注意要全局唯一,如果設備無法做到,可以在編解碼時通過添加前綴等方式實現.
消息定義:
ReadPropertyMessage{
String deviceId;
String messageId;
List<String> properties;//可讀取多個屬性
}
ReadPropertyMessageReply{
String deviceId;
String messageId;
long timestamp;
boolean success;
Map<String,Object> properties;//屬性鍵值對
}
WritePropertyMessage{
String deviceId;
String messageId;
Map<String,Object> properties;
}
WritePropertyMessageReply{
String deviceId;
String messageId;
long timestamp;
boolean success;
Map<String,Object> properties; //回覆被修改的屬性最新值
}
ReportPropertyMessage{
String deviceId;
String messageId;
long timestamp;
Map<String,Object> properties;
}
功能相關消息
調用設備功能到消息(FunctionInvokeMessage
)由平臺發往設備,對應到返回消息FunctionInvokeMessageReply
.
消息定義:
FunctionInvokeMessage{
String functionId;//功能標識,在元數據中定義.
String deviceId;
String messageId;
List<FunctionParameter> inputs;//輸入參數
}
FunctionParameter{
String name;
Object value;
}
FunctionInvokeMessageReply{
String deviceId;
String messageId;
long timestamp;
boolean success;
Object output; //輸出值,需要與元數據定義中的類型一致
}
事件消息
事件消息EventMessage
由設備端發往平臺.
消息定義:
EventMessage{
String event; //事件標識,在元數據中定義
Object data; //與元數據中定義的類型一致,如果是對象類型,請轉爲java.util.HashMap,禁止使用自定義類型.
long timestamp;
}
其他消息
DeviceOnlineMessage
設備上線消息,通常用於網關代理的子設備的上線操作.DeviceOfflineMessage
設備上線消息,通常用於網關代理的子設備的下線操作.ChildrenDeviceMessage
子設備消息,通常用於網關代理的子設備的消息.ChildrenDeviceMessageReply
子設備消息回覆,用於平臺向網關代理的子設備發送消息後設備回覆給平臺的結果.
消息定義:
DeviceOnlineMessage{
String deviceId;
long timestamp;
}
DeviceOfflineMessage{
String deviceId;
long timestamp;
}
ChildDeviceMessage{
String deviceId;
String childDeviceId;
Message childDeviceMessage; //子設備消息
}
父子設備消息處理請看這裏
消息發送攔截器
使用攔截器可以攔截消息發送和返回的動作,通過修改參數等操作實現自定義邏輯,如: 當設備離線時,將消息緩存到設備配置中,等設備上線時再重發.
DeviceMessageSenderInterceptor{
//發送前
Mono<DeviceMessage> preSend(DeviceOperator device, DeviceMessage message);
//發送後
<R extends DeviceMessage> Flux<R> afterSent(DeviceOperator device, DeviceMessage message, Flux<R> reply);
}
在發送前,可以將參數DeviceMessage
轉爲其他消息.
發送後,會將返回結果流Flux<R>
傳入,通過對該數據流對操作以實現自定義行爲,如:忽略錯誤等.
配置元數據
配置元數據用於告訴平臺,在使用此協議的時候,需要添加一些自定義配置到設備配置(DeviceOperator.setConfig
)中.
在其他地方可以通過DeviceOperator.getConfig
獲取這些配置.
例如:
CompositeProtocolSupport support = new CompositeProtocolSupport();
support.setId("demo-v1");
support.setName("演示協議v1");
support.setDescription("演示協議");
support.setMetadataCodec(new JetLinksDeviceMetadataCodec()); //固定爲JetLinksDeviceMetadataCodec,請勿修改.
DefaultConfigMetadata mqttConfig = new DefaultConfigMetadata(
"MQTT認證配置"
, "")
.add("username", "username", "MQTT用戶名", new StringType())
.add("password", "password", "MQTT密碼", new PasswordType());
//設置MQTT所需要到配置
support.addConfigMetadata(DefaultTransport.MQTT, mqttConfig);