commit 319e00b1cebb1f291b88f568228bf900628a0e47
Author: accpt27chen/zhangq <13631262097@139.com>
Date: Wed Oct 12 14:06:24 2022 +0800
初始化
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..619eaaa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,200 @@
+# 标准消息编解码协议
+终端设备可以根据此标准消息解码协议进行对接物联平台
+
+## 标准协议Topic
+```
+ * 标准编解码器
+ *
+ * 包含:码库、编码、解码
+ * 码库:产品标识+设备标识+码库特定义
+ * 如:属性上报: 产品标识+设备标识+ /properties/report
+ *
+ * 注释:
+ * A.默认为标准物模型数据格式,这直接 payload.toJavaObject(type) 转换
+ * B.非标准物模型数据格式,则重写相应的doEncode、doDecode方法,进行标准物模型数据格式转换
+ *
+ *
+ *
+ * 下行Topic:
+ * 读取设备属性: /{productId}/{deviceId}/properties/read
+ * 修改设备属性: /{productId}/{deviceId}/properties/write
+ * 调用设备功能: /{productId}/{deviceId}/function/invoke
+ *
+ * //网关设备
+ * 读取子设备属性: /{productId}/{deviceId}/child/{childDeviceId}/properties/read
+ * 修改子设备属性: /{productId}/{deviceId}/child/{childDeviceId}/properties/write
+ * 调用子设备功能: /{productId}/{deviceId}/child/{childDeviceId}/function/invoke
+ *
+ * 上行Topic:
+ * 读取属性回复: /{productId}/{deviceId}/properties/read/reply
+ * 修改属性回复: /{productId}/{deviceId}/properties/write/reply
+ * 调用设备功能: /{productId}/{deviceId}/function/invoke/reply
+ * 上报设备事件: /{productId}/{deviceId}/event/{eventId}
+ * 上报设备属性: /{productId}/{deviceId}/properties/report
+ * 上报设备派生物模型: /{productId}/{deviceId}/metadata/derived
+ *
+ * //网关设备
+ * 子设备上线消息: /{productId}/{deviceId}/child/{childDeviceId}/connected
+ * 子设备下线消息: /{productId}/{deviceId}/child/{childDeviceId}/disconnect
+ * 读取子设备属性回复: /{productId}/{deviceId}/child/{childDeviceId}/properties/read/reply
+ * 修改子设备属性回复: /{productId}/{deviceId}/child/{childDeviceId}/properties/write/reply
+ * 调用子设备功能回复: /{productId}/{deviceId}/child/{childDeviceId}/function/invoke/reply
+ * 上报子设备事件: /{productId}/{deviceId}/child/{childDeviceId}/event/{eventId}
+ * 上报子设备派生物模型: /{productId}/{deviceId}/child/{childDeviceId}/metadata/derived
+ *
+ *
+ * 基于jet links 的消息编解码器
+```
+
+## 支持协议
+* V1.0 支持MQTT
+
+## 使用
+
+使用命名`mvn package` 打包后, 通过jetlinks管理界面`设备管理-协议管理`中上传jar包.
+类名填写: `cn.flyrise.iot.protocol.PaiProtocolSupportProvider`
+
+## 扩展开发
+pai-official-protocol工程也可以当作是标准协议骨架,可进行自定义编解码协议的开发
+### Topic主题不变-只是进行数据转换
+
+* 非标准物模型数据格式,则重写相应的doEncode、doDecode方法,进行标准物模型数据格式转换 (TopicMessageCodec)
+
+```
+ //事件上报 - 转行标准数据
+ event("/*/event/*", EventMessage.class){
+ @Override
+ DeviceMessage doDecode(DeviceOperator device, String[] topic, JSONObject payload){
+ EventMessage eventMessage = payload.toJavaObject(EventMessage.class);
+ eventMessage.setEvent(topic[topic.length - 1]);
+ return eventMessage;
+ }
+ },
+```
+* 对应协议编解码器也需要进行相应调整,如MQTT(FlyriseMqttDeviceMessageCodec)
+
+```
+package cn.flyrise.iot.protocol.agree.mqtt;
+
+import cn.flyrise.iot.protocol.topic.TopicMessage;
+import cn.flyrise.iot.protocol.topic.TopicMessageCodec;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import io.netty.buffer.Unpooled;
+import lombok.extern.slf4j.Slf4j;
+import org.jetlinks.core.device.DeviceConfigKey;
+import org.jetlinks.core.device.DeviceOperator;
+import org.jetlinks.core.message.DeviceMessage;
+import org.jetlinks.core.message.DisconnectDeviceMessage;
+import org.jetlinks.core.message.Message;
+import org.jetlinks.core.message.codec.*;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.Nonnull;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * MQTT 编解码器
+ * @author zhangqiang
+ * @since 0.1
+ */
+@Slf4j
+public class FlyriseMqttDeviceMessageCodec implements DeviceMessageCodec {
+
+ private final Transport transport;
+
+ public FlyriseMqttDeviceMessageCodec(Transport transport) {
+ this.transport = transport;
+ }
+
+ public FlyriseMqttDeviceMessageCodec() {
+ this(DefaultTransport.MQTT);
+ }
+
+ @Override
+ public Transport getSupportTransport() {
+ return transport;
+ }
+
+ /**
+ * MQTT 编码器
+ * @param context
+ * @return
+ */
+ @Nonnull
+ public Mono encode(@Nonnull MessageEncodeContext context) {
+
+ Message message = context.getMessage();
+ return Mono.defer(() -> {
+ if (message instanceof DeviceMessage) {
+ // 断开连接操作
+ if (message instanceof DisconnectDeviceMessage) {
+ return ((ToDeviceMessageContext) context)
+ .disconnect()
+ .then(Mono.empty());
+ }
+
+ DeviceMessage deviceMessage = (DeviceMessage) message;
+ TopicMessage msg = TopicMessageCodec.encode(deviceMessage);
+ if (null == msg) {
+ return Mono.empty();
+ }
+
+ log.info("pai-official-protocol TopicMessage:{}",JSONObject.toJSONString(msg));
+ return Mono
+ .justOrEmpty(deviceMessage.getHeader("productId").map(String::valueOf)) //是否有产品信息
+ .switchIfEmpty(context.getDevice(deviceMessage.getDeviceId())
+ .flatMap(device -> device.getSelfConfig(DeviceConfigKey.productId))
+ )
+ .defaultIfEmpty("null")
+ .map(productId -> SimpleMqttMessage
+ .builder()
+ .clientId(deviceMessage.getDeviceId())
+ .topic("/".concat(productId).concat(msg.getTopic()))
+ .payloadType(MessagePayloadType.JSON)
+ .payload(Unpooled.wrappedBuffer(JSON.toJSONBytes(msg.getMessage())))
+ .build());
+ }
+ return Mono.empty();
+
+ });
+ }
+
+ /**
+ * MQTT 解码器
+ * @param context
+ * @return
+ */
+ @Nonnull
+ @Override
+ public Mono extends Message> decode(@Nonnull MessageDecodeContext context) {
+
+ return Mono.fromSupplier(() -> {
+ // 转为mqtt消息
+ MqttMessage mqttMessage = (MqttMessage) context.getMessage();
+ // 消息主题
+ String topic = mqttMessage.getTopic();
+ // 消息体转为json
+ JSONObject payload = JSON.parseObject(mqttMessage.getPayload().toString(StandardCharsets.UTF_8));
+ DeviceOperator deviceOperator = context.getDevice();
+ return TopicMessageCodec.decode(deviceOperator,topic, payload);
+ });
+ }
+
+}
+
+```
+
+### Topic主题变更
+* 自己定义的Topoc 需要对应 标准协议Topic对应的物模型数据结构
+```
+ //上报属性数据
+ reportProperty("/*/properties/report", ReportPropertyMessage.class){
+ // to do 自定义解码器 doDecode(DeviceOperator device, String topic, JSONObject payload)
+ },
+```
+* 对应协议编解码器也需要进行相应调整,如MQTT(FlyriseMqttDeviceMessageCodec)
+
+
+
+
diff --git a/pai-subsystem-protocol.iml b/pai-subsystem-protocol.iml
new file mode 100644
index 0000000..8021953
--- /dev/null
+++ b/pai-subsystem-protocol.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cdd375f
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,116 @@
+
+
+ 4.0.0
+
+ cn.flyrise.iot.protocol
+ pai-official-protocol
+ 1.0
+
+ UTF-8
+ zh_CN
+ 1.8
+ ${java.version}
+ 4.1.51.Final
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ ${project.build.jdk}
+ ${project.build.jdk}
+ ${project.build.sourceEncoding}
+
+
+
+
+
+
+
+ org.jetlinks
+ jetlinks-core
+ 1.1.9
+
+
+ org.jetlinks
+ jetlinks-supports
+ 1.1.6
+
+
+ org.eclipse.californium
+ californium-core
+ 2.2.1
+ true
+
+
+ org.projectlombok
+ lombok
+ 1.18.10
+
+
+ io.vertx
+ vertx-core
+ 3.8.3
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.5.2
+ test
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+
+
+ io.vertx
+ vertx-core
+ 3.8.3
+ compile
+
+
+
+ org.springframework
+ spring-webflux
+ 5.2.5.RELEASE
+
+
+
+
+
+
+
+ io.netty
+ netty-bom
+ ${netty.version}
+ pom
+ import
+
+
+
+
+
+
+ hsweb-nexus
+ Nexus Release Repository
+ http://nexus.hsweb.me/content/groups/public/
+
+ true
+ always
+
+
+
+ aliyun-nexus
+ aliyun
+ http://maven.aliyun.com/nexus/content/groups/public/
+
+
+
\ No newline at end of file
diff --git a/src/main/java/cn/flyrise/iot/protocol/PaiProtocolSupportProvider.java b/src/main/java/cn/flyrise/iot/protocol/PaiProtocolSupportProvider.java
new file mode 100644
index 0000000..0b81087
--- /dev/null
+++ b/src/main/java/cn/flyrise/iot/protocol/PaiProtocolSupportProvider.java
@@ -0,0 +1,153 @@
+package cn.flyrise.iot.protocol;
+
+import cn.flyrise.iot.protocol.agree.coap.PaiCoapDTLSDeviceMessageCodec;
+import cn.flyrise.iot.protocol.agree.coap.PaiCoapDeviceMessageCodec;
+import cn.flyrise.iot.protocol.agree.http.PaiHttpAuthenticator;
+import cn.flyrise.iot.protocol.agree.http.PaiHttpDeviceMessageCodec;
+import cn.flyrise.iot.protocol.agree.mqtt.PaiMqttAuthenticator;
+import cn.flyrise.iot.protocol.agree.mqtt.PaiMqttDeviceMessageCodec;
+import cn.flyrise.iot.protocol.agree.websocket.PaiWebsocketDeviceMessageCodec;
+import org.jetlinks.core.ProtocolSupport;
+import org.jetlinks.core.defaults.CompositeProtocolSupport;
+import org.jetlinks.core.message.codec.DefaultTransport;
+import org.jetlinks.core.metadata.DefaultConfigMetadata;
+import org.jetlinks.core.metadata.DeviceConfigScope;
+import org.jetlinks.core.metadata.types.EnumType;
+import org.jetlinks.core.metadata.types.PasswordType;
+import org.jetlinks.core.metadata.types.StringType;
+import org.jetlinks.core.spi.ProtocolSupportProvider;
+import org.jetlinks.core.spi.ServiceContext;
+import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec;
+import reactor.core.publisher.Mono;
+
+/**
+ * 物联协议
+ * Flyrise-MQTT
+ * @author zhangqiang
+ * @since 0.1
+ */
+public class PaiProtocolSupportProvider implements ProtocolSupportProvider {
+
+
+ @Override
+ public void dispose() {
+ //协议卸载时执行
+ }
+
+ /**
+ * MQTT 配置信息
+ */
+ private static final DefaultConfigMetadata mqttConfig = new DefaultConfigMetadata(
+ "MQTT认证配置"
+ , "MQTT客户端clentId为设备id")
+ .add("username", "username", "MQTT用户名", StringType.GLOBAL)
+ .add("password", "password", "MQTT密码", PasswordType.GLOBAL)
+ .add("clientid", "clientid", "MQTT客户端ID", StringType.GLOBAL)
+ .add("host", "host", "设备host地址", StringType.GLOBAL);
+
+
+ /**
+ * COAP 配置信息
+ */
+ private static final DefaultConfigMetadata coapConfig = new DefaultConfigMetadata(
+ "CoAP认证配置",
+ "使用CoAP进行数据上报时,需要对数据进行加密:" +
+ "encrypt(payload,secureKey);")
+ .add("encAlg", "加密算法", "加密算法", new EnumType()
+ .addElement(EnumType.Element.of("AES", "AES加密(ECB,PKCS#5)", "加密模式:ECB,填充方式:PKCS#5")), DeviceConfigScope.product)
+ .add("secureKey", "密钥", "16位密钥KEY", new PasswordType());
+
+ /**
+ * COAPDTLS 配置信息
+ */
+ private static final DefaultConfigMetadata coapDTLSConfig = new DefaultConfigMetadata(
+ "CoAP DTLS配置",
+ "使用CoAP DTLS 进行数据上报需要先进行签名认证获取token.\n" +
+ "之后上报数据需要在Option中携带token信息. \n" +
+ "自定义Option: 2110,sign ; 2111,token ")
+ .add("secureKey", "密钥", "认证签名密钥", new PasswordType());
+
+
+ /**
+ * MQTT 配置信息
+ */
+ private static final DefaultConfigMetadata httpConfig = new DefaultConfigMetadata(
+ "HTTP认证配置"
+ , "设备请求Authorization为设备id")
+ .add("authorization", "authorization", "授权信息", StringType.GLOBAL);
+
+
+ @Override
+ public Mono extends ProtocolSupport> create(ServiceContext context) {
+ CompositeProtocolSupport support = new CompositeProtocolSupport();
+
+ // 编解码协议基础信息
+ support.setId("pai-official-protocol");
+ support.setName("pai-official-protocol");
+ support.setDescription("pai-official-protocol Version 1.0");
+ // 固定为JetLinksDeviceMetadataCodec
+ support.setMetadataCodec(new JetLinksDeviceMetadataCodec());
+
+ // 如下配置支持的协议(agree目录下开发的协议) 支持协议: MQTT 、COAP 、COAPDTLS 、HTTP
+
+ // MQTT ===========================================================================================================
+ {
+ // MQTT认证策略
+ support.addAuthenticator(DefaultTransport.MQTT, new PaiMqttAuthenticator());
+ support.addAuthenticator(DefaultTransport.MQTT_TLS, new PaiMqttAuthenticator());
+ // MQTT需要的配置信息
+ support.addConfigMetadata(DefaultTransport.MQTT, mqttConfig);
+ support.addConfigMetadata(DefaultTransport.MQTT_TLS, mqttConfig);
+ // MQTT消息解码器
+ support.addMessageCodecSupport(new PaiMqttDeviceMessageCodec(DefaultTransport.MQTT));
+ support.addMessageCodecSupport(new PaiMqttDeviceMessageCodec(DefaultTransport.MQTT_TLS));
+ }
+
+ // COAP ===========================================================================================================
+ {
+ // COAP认证策略 (COAP使用数据加解密认证,CoAP DTLS使用签名认证)
+ // COAP需要的配置信息
+ support.addConfigMetadata(DefaultTransport.CoAP, coapConfig);
+ support.addConfigMetadata(DefaultTransport.CoAP_DTLS, coapDTLSConfig);
+ // COAP消息解码器
+ support.addMessageCodecSupport(new PaiCoapDeviceMessageCodec());
+ support.addMessageCodecSupport(new PaiCoapDTLSDeviceMessageCodec());
+ }
+
+ // HTTP ===========================================================================================================
+ {
+ // HTTP认证策略
+ support.addAuthenticator(DefaultTransport.HTTP, new PaiHttpAuthenticator());
+ support.addAuthenticator(DefaultTransport.HTTPS, new PaiHttpAuthenticator());
+ // HTTP需要的配置信息
+ support.addConfigMetadata(DefaultTransport.HTTP, httpConfig);
+ // HTTP消息解码器
+ support.addMessageCodecSupport(new PaiHttpDeviceMessageCodec(DefaultTransport.HTTP));
+ support.addMessageCodecSupport(new PaiHttpDeviceMessageCodec(DefaultTransport.HTTPS));
+ }
+
+ // TCP ===========================================================================================================
+ {
+ // TCP认证策略
+ // TCP需要的配置信息
+ // TCP消息解码器
+ }
+
+ // UDP ===========================================================================================================
+ {
+ // UDP认证策略
+ // UDP需要的配置信息
+ // UDP消息解码器
+ }
+
+ // WebSocket ======================================================================================================
+ {
+ // WebSocket认证策略
+ // WebSocket需要的配置信息
+ // WebSocket消息解码器
+ support.addMessageCodecSupport(new PaiWebsocketDeviceMessageCodec());
+ }
+
+ return Mono.just(support);
+ }
+}
diff --git a/src/main/java/cn/flyrise/iot/protocol/agree/coap/AbstractCoapDeviceMessageCodec.java b/src/main/java/cn/flyrise/iot/protocol/agree/coap/AbstractCoapDeviceMessageCodec.java
new file mode 100644
index 0000000..cb3c835
--- /dev/null
+++ b/src/main/java/cn/flyrise/iot/protocol/agree/coap/AbstractCoapDeviceMessageCodec.java
@@ -0,0 +1,97 @@
+package cn.flyrise.iot.protocol.agree.coap;
+
+import cn.flyrise.iot.protocol.topic.TopicMessageCodec;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.californium.core.coap.CoAP;
+import org.jetlinks.core.message.DeviceMessage;
+import org.jetlinks.core.message.codec.*;
+import org.reactivestreams.Publisher;
+import org.springframework.util.StringUtils;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import javax.annotation.Nonnull;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * COAP 默认编解码器
+ *
+ * 目前COAP只支持上行,所以只需要解码处理
+ */
+@Slf4j
+public abstract class AbstractCoapDeviceMessageCodec implements DeviceMessageCodec {
+
+ protected abstract Flux decode(CoapMessage message, MessageDecodeContext context, Consumer