From 59c981bf9568e35f7795d7156b9689abc81b704a Mon Sep 17 00:00:00 2001 From: zhou-hao Date: Thu, 21 Jan 2021 11:21:28 +0800 Subject: [PATCH 01/15] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../official/JetlinksTopicMessageCodec.java | 17 +++++++------- .../JetLinksMqttDeviceMessageCodecTest.java | 22 +++++++++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java index 762259b..744a793 100644 --- a/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java +++ b/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java @@ -11,7 +11,6 @@ import org.jetlinks.core.message.function.FunctionInvokeMessage; import org.jetlinks.core.message.function.FunctionInvokeMessageReply; import org.jetlinks.core.message.property.*; import org.jetlinks.core.utils.TopicUtils; -import org.jetlinks.supports.utils.MqttTopicUtils; import org.springframework.util.Assert; import java.util.Map; @@ -86,7 +85,7 @@ class JetlinksTopicMessageCodec { Assert.notNull(message, "message can not be null"); if (message instanceof ReadPropertyMessage) { - String topic = "/" .concat(deviceId).concat("/properties/read"); + String topic = "/".concat(deviceId).concat("/properties/read"); JSONObject mqttData = new JSONObject(); mqttData.put("messageId", message.getMessageId()); mqttData.put("properties", ((ReadPropertyMessage) message).getProperties()); @@ -94,7 +93,7 @@ class JetlinksTopicMessageCodec { return new EncodedTopic(topic, mqttData); } else if (message instanceof WritePropertyMessage) { - String topic = "/" .concat(deviceId).concat("/properties/write"); + String topic = "/".concat(deviceId).concat("/properties/write"); JSONObject mqttData = new JSONObject(); mqttData.put("messageId", message.getMessageId()); mqttData.put("properties", ((WritePropertyMessage) message).getProperties()); @@ -102,7 +101,7 @@ class JetlinksTopicMessageCodec { return new EncodedTopic(topic, mqttData); } else if (message instanceof FunctionInvokeMessage) { - String topic = "/" .concat(deviceId).concat("/function/invoke"); + String topic = "/".concat(deviceId).concat("/function/invoke"); FunctionInvokeMessage invokeMessage = ((FunctionInvokeMessage) message); JSONObject mqttData = new JSONObject(); mqttData.put("messageId", message.getMessageId()); @@ -112,7 +111,7 @@ class JetlinksTopicMessageCodec { return new EncodedTopic(topic, mqttData); } else if (message instanceof UpgradeFirmwareMessage) { - String topic = "/" .concat(deviceId).concat("/firmware/upgrade"); + String topic = "/".concat(deviceId).concat("/firmware/upgrade"); UpgradeFirmwareMessage firmwareMessage = ((UpgradeFirmwareMessage) message); JSONObject mqttData = new JSONObject(); mqttData.put("messageId", message.getMessageId()); @@ -125,13 +124,13 @@ class JetlinksTopicMessageCodec { return new EncodedTopic(topic, mqttData); } else if (message instanceof ReadFirmwareMessage) { - String topic = "/" .concat(deviceId).concat("/firmware/read"); + String topic = "/".concat(deviceId).concat("/firmware/read"); JSONObject mqttData = new JSONObject(); mqttData.put("messageId", message.getMessageId()); mqttData.put("deviceId", deviceId); return new EncodedTopic(topic, mqttData); } else if (message instanceof RequestFirmwareMessageReply) { - String topic = "/" .concat(deviceId).concat("/firmware/pull/reply"); + String topic = "/".concat(deviceId).concat("/firmware/pull/reply"); RequestFirmwareMessageReply firmwareMessage = ((RequestFirmwareMessageReply) message); JSONObject mqttData = new JSONObject(); mqttData.put("messageId", message.getMessageId()); @@ -145,7 +144,7 @@ class JetlinksTopicMessageCodec { } else if (message instanceof ChildDeviceMessage) { ChildDeviceMessage childDeviceMessage = ((ChildDeviceMessage) message); EncodedTopic result = encode(childDeviceMessage.getChildDeviceId(), childDeviceMessage.getChildDeviceMessage()); - String topic = "/" .concat(deviceId).concat("/child").concat(result.topic); + String topic = "/".concat(deviceId).concat("/child").concat(result.topic); result.payload.put("deviceId", childDeviceMessage.getChildDeviceId()); return new EncodedTopic(topic, result.payload); @@ -180,7 +179,7 @@ class JetlinksTopicMessageCodec { message = object.toJavaObject(ReportFirmwareMessage.class); } else if (result.isUpgradeFirmwareProgress()) { message = object.toJavaObject(UpgradeFirmwareProgressMessage.class); - }else if (topic.endsWith("connected")) { + } else if (topic.endsWith("connected")) { message = object.toJavaObject(DeviceOnlineMessage.class); } else if (topic.endsWith("disconnect")) { message = object.toJavaObject(DeviceOfflineMessage.class); diff --git a/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java b/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java index 2079c84..ea3314b 100644 --- a/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java +++ b/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java @@ -180,10 +180,9 @@ public class JetLinksMqttDeviceMessageCodecTest { .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes())) .build())).block(); - Assert.assertTrue(message instanceof ChildDeviceMessageReply); - ChildDeviceMessageReply childReply = ((ChildDeviceMessageReply) message); + Assert.assertTrue(message instanceof ChildDeviceMessage); + ChildDeviceMessage childReply = ((ChildDeviceMessage) message); - Assert.assertTrue(childReply.isSuccess()); Assert.assertEquals(childReply.getDeviceId(),"device1"); Assert.assertEquals(childReply.getMessageId(),"test"); @@ -251,10 +250,9 @@ public class JetLinksMqttDeviceMessageCodecTest { .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"output\":\"ok\"}".getBytes())) .build())).block(); - Assert.assertTrue(message instanceof ChildDeviceMessageReply); - ChildDeviceMessageReply childReply = ((ChildDeviceMessageReply) message); + Assert.assertTrue(message instanceof ChildDeviceMessage); + ChildDeviceMessage childReply = ((ChildDeviceMessage) message); - Assert.assertTrue(childReply.isSuccess()); Assert.assertEquals(childReply.getDeviceId(),"device1"); Assert.assertEquals(childReply.getMessageId(),"test"); @@ -288,9 +286,9 @@ public class JetLinksMqttDeviceMessageCodecTest { .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"data\":100}".getBytes())) .build())).block(); - Assert.assertTrue(message instanceof ChildDeviceMessageReply); + Assert.assertTrue(message instanceof ChildDeviceMessage); - EventMessage reply = ((EventMessage) ((ChildDeviceMessageReply) message).getChildDeviceMessage()); + EventMessage reply = ((EventMessage) ((ChildDeviceMessage) message).getChildDeviceMessage()); Assert.assertEquals(reply.getDeviceId(), "test"); Assert.assertEquals(reply.getMessageId(), "test"); Assert.assertEquals(reply.getData(), 100); @@ -320,9 +318,9 @@ public class JetLinksMqttDeviceMessageCodecTest { .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes())) .build())).block(); - Assert.assertTrue(message instanceof ChildDeviceMessageReply); + Assert.assertTrue(message instanceof ChildDeviceMessage); - ReportPropertyMessage reply = ((ReportPropertyMessage) ((ChildDeviceMessageReply) message).getChildDeviceMessage()); + ReportPropertyMessage reply = ((ReportPropertyMessage) ((ChildDeviceMessage) message).getChildDeviceMessage()); Assert.assertEquals(reply.getDeviceId(), "test"); Assert.assertEquals(reply.getMessageId(), "test"); Assert.assertEquals(reply.getProperties(), Collections.singletonMap("sn", "test")); @@ -352,9 +350,9 @@ public class JetLinksMqttDeviceMessageCodecTest { .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"metadata\":\"1\"}".getBytes())) .build())).block(); - Assert.assertTrue(message instanceof ChildDeviceMessageReply); + Assert.assertTrue(message instanceof ChildDeviceMessage); - DerivedMetadataMessage reply = ((DerivedMetadataMessage) ((ChildDeviceMessageReply) message).getChildDeviceMessage()); + DerivedMetadataMessage reply = ((DerivedMetadataMessage) ((ChildDeviceMessage) message).getChildDeviceMessage()); Assert.assertEquals(reply.getDeviceId(), "test"); Assert.assertEquals(reply.getMessageId(), "test"); Assert.assertEquals(reply.getMetadata(), "1"); From c642d17f4eb549f83c5e4c4f41377e879af826f3 Mon Sep 17 00:00:00 2001 From: zhou-hao Date: Thu, 21 Jan 2021 16:43:37 +0800 Subject: [PATCH 02/15] 2.0-SNAPSHOT --- pom.xml | 39 ++- .../JetLinksCoapDTLSDeviceMessageCodec.java | 64 ++-- .../JetLinksCoapDeviceMessageCodec.java | 46 +-- .../JetLinksMqttDeviceMessageCodec.java | 38 ++- .../JetLinksProtocolSupportProvider.java | 6 +- .../official/JetlinksTopicMessageCodec.java | 289 ------------------ .../protocol/official/ObjectMappers.java | 23 ++ .../protocol/official/TopicMessageCodec.java | 191 ++++++++++++ .../protocol/official/TopicPayload.java | 15 + ....jetlinks.core.spi.ProtocolSupportProvider | 1 - .../JetLinksCoapDeviceMessageCodecTest.java | 42 +-- .../JetLinksMqttDeviceMessageCodecTest.java | 35 +-- .../JetlinksTopicMessageCodecTest.java | 24 -- .../official/TopicMessageCodecTest.java | 44 +++ src/test/resources/logback.xml | 15 + 15 files changed, 447 insertions(+), 425 deletions(-) delete mode 100644 src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java create mode 100644 src/main/java/org/jetlinks/protocol/official/ObjectMappers.java create mode 100644 src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java create mode 100644 src/main/java/org/jetlinks/protocol/official/TopicPayload.java delete mode 100644 src/main/resources/services/org.jetlinks.core.spi.ProtocolSupportProvider delete mode 100644 src/test/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodecTest.java create mode 100644 src/test/java/org/jetlinks/protocol/official/TopicMessageCodecTest.java create mode 100644 src/test/resources/logback.xml diff --git a/pom.xml b/pom.xml index f716adf..4300c9e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.jetlinks jetlinks-official-protocol - 1.0-SNAPSHOT + 2.0-SNAPSHOT JetLinks http://jetlinks.org @@ -69,7 +69,7 @@ org.jetlinks jetlinks-supports - 1.1.4-SNAPSHOT + 1.1.5 @@ -86,6 +86,11 @@ provided + + com.fasterxml.jackson.dataformat + jackson-dataformat-cbor + + junit junit @@ -93,13 +98,39 @@ test + + io.projectreactor + reactor-test + 3.3.12.RELEASE + test + + + + + ch.qos.logback + logback-classic + 1.2.3 + test + + + + + com.fasterxml.jackson + jackson-bom + 2.11.4 + pom + import + + + + hsweb-nexus Nexus Release Repository - http://nexus.hsweb.me/content/groups/public/ + https://nexus.hsweb.me/content/groups/public/ true always @@ -108,7 +139,7 @@ aliyun-nexus aliyun - http://maven.aliyun.com/nexus/content/groups/public/ + https://maven.aliyun.com/nexus/content/groups/public/ diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDTLSDeviceMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDTLSDeviceMessageCodec.java index 549df66..8dc0806 100644 --- a/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDTLSDeviceMessageCodec.java +++ b/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDTLSDeviceMessageCodec.java @@ -1,22 +1,25 @@ package org.jetlinks.protocol.official; -import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.OptionNumberRegistry; import org.hswebframework.web.id.IDGenerator; -import org.jetlinks.core.message.Message; +import org.jetlinks.core.message.DeviceMessage; import org.jetlinks.core.message.codec.*; +import org.springframework.http.MediaType; import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.annotation.Nonnull; -import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.function.Consumer; @Slf4j -public class JetLinksCoapDTLSDeviceMessageCodec extends JetlinksTopicMessageCodec implements DeviceMessageCodec { +public class JetLinksCoapDTLSDeviceMessageCodec implements DeviceMessageCodec { @Override public Transport getSupportTransport() { @@ -24,15 +27,22 @@ public class JetLinksCoapDTLSDeviceMessageCodec extends JetlinksTopicMessageCode } - public Mono decode(CoapMessage message, MessageDecodeContext context, Consumer response) { - return Mono.defer(() -> { + public Flux decode(CoapMessage message, MessageDecodeContext context, Consumer response) { + return Flux.defer(() -> { String path = message.getPath(); String sign = message.getStringOption(2110).orElse(null); String token = message.getStringOption(2111).orElse(null); - String payload = message.getPayload().toString(StandardCharsets.UTF_8); + byte[] payload = message.payloadAsBytes(); + boolean cbor = message + .getStringOption(OptionNumberRegistry.CONTENT_FORMAT) + .map(MediaType::valueOf) + .map(MediaType.APPLICATION_CBOR::includes) + .orElse(false); + ObjectMapper objectMapper = cbor ? ObjectMappers.CBOR_MAPPER : ObjectMappers.JSON_MAPPER; if ("/auth".equals(path)) { //认证 - return context.getDevice() + return context + .getDevice() .getConfig("secureKey") .flatMap(sk -> { String secureKey = sk.asString(); @@ -42,12 +52,12 @@ public class JetLinksCoapDTLSDeviceMessageCodec extends JetlinksTopicMessageCode } String newToken = IDGenerator.MD5.generate(); return context.getDevice() - .setConfig("coap-token", newToken) - .doOnSuccess(success -> { - JSONObject json = new JSONObject(); - json.put("token", newToken); - response.accept(json.toJSONString()); - }); + .setConfig("coap-token", newToken) + .doOnSuccess(success -> { + JSONObject json = new JSONObject(); + json.put("token", newToken); + response.accept(json.toJSONString()); + }); }) .then(Mono.empty()); } @@ -55,22 +65,21 @@ public class JetLinksCoapDTLSDeviceMessageCodec extends JetlinksTopicMessageCode response.accept(CoAP.ResponseCode.UNAUTHORIZED); return Mono.empty(); } - return context.getDevice() + return context + .getDevice() .getConfig("coap-token") - .switchIfEmpty(Mono.fromRunnable(() -> { - response.accept(CoAP.ResponseCode.UNAUTHORIZED); - })) - .flatMap(value -> { + .switchIfEmpty(Mono.fromRunnable(() -> response.accept(CoAP.ResponseCode.UNAUTHORIZED))) + .flatMapMany(value -> { String tk = value.asString(); if (!token.equals(tk)) { response.accept(CoAP.ResponseCode.UNAUTHORIZED); return Mono.empty(); } - return Mono - .just(decode(path, JSON.parseObject(payload)).getMessage()) + return TopicMessageCodec + .decode(objectMapper, TopicMessageCodec.removeProductPath(path), payload) .switchIfEmpty(Mono.fromRunnable(() -> response.accept(CoAP.ResponseCode.BAD_REQUEST))); }) - .doOnSuccess(msg -> { + .doOnComplete(() -> { response.accept(CoAP.ResponseCode.CREATED); }) .doOnError(error -> { @@ -83,7 +92,7 @@ public class JetLinksCoapDTLSDeviceMessageCodec extends JetlinksTopicMessageCode @Nonnull @Override - public Mono decode(@Nonnull MessageDecodeContext context) { + public Flux decode(@Nonnull MessageDecodeContext context) { if (context.getMessage() instanceof CoapExchangeMessage) { CoapExchangeMessage exchangeMessage = ((CoapExchangeMessage) context.getMessage()); return decode(exchangeMessage, context, resp -> { @@ -101,12 +110,15 @@ public class JetLinksCoapDTLSDeviceMessageCodec extends JetlinksTopicMessageCode }); } - return Mono.empty(); + return Flux.empty(); } - protected boolean verifySign(String secureKey, String deviceId, String payload, String sign) { + protected boolean verifySign(String secureKey, String deviceId, byte[] payload, String sign) { //验证签名 - if (StringUtils.isEmpty(secureKey) || !DigestUtils.md5Hex(payload.concat(secureKey)).equalsIgnoreCase(sign)) { + byte[] secureKeyBytes = secureKey.getBytes(); + byte[] signPayload = Arrays.copyOf(payload, payload.length + secureKeyBytes.length); + System.arraycopy(secureKeyBytes, 0, signPayload, 0, secureKeyBytes.length); + if (StringUtils.isEmpty(secureKey) || !DigestUtils.md5Hex(signPayload).equalsIgnoreCase(sign)) { log.info("device [{}] coap sign [{}] error, payload:{}", deviceId, sign, payload); return false; } diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodec.java index 6d206cb..5c78ae5 100644 --- a/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodec.java +++ b/src/main/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodec.java @@ -2,53 +2,63 @@ package org.jetlinks.protocol.official; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBuf; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.OptionNumberRegistry; import org.eclipse.californium.core.server.resources.CoapExchange; import org.jetlinks.core.Value; -import org.jetlinks.core.message.Message; +import org.jetlinks.core.message.DeviceMessage; import org.jetlinks.core.message.codec.*; import org.jetlinks.protocol.official.cipher.Ciphers; +import org.springframework.http.MediaType; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.annotation.Nonnull; @Slf4j -public class JetLinksCoapDeviceMessageCodec extends JetlinksTopicMessageCodec implements DeviceMessageCodec { - +public class JetLinksCoapDeviceMessageCodec implements DeviceMessageCodec { @Override public Transport getSupportTransport() { return DefaultTransport.CoAP; } - protected JSONObject decode(String text) { - return JSON.parseObject(text); - } + protected Flux decode(CoapMessage message, MessageDecodeContext context) { + String path = message.getPath(); - protected Mono decode(CoapMessage message, MessageDecodeContext context) { - String path = message.getPath(); + boolean cbor = message + .getStringOption(OptionNumberRegistry.CONTENT_FORMAT) + .map(MediaType::valueOf) + .map(MediaType.APPLICATION_CBOR::includes) + .orElse(false); + ObjectMapper objectMapper = cbor ? ObjectMappers.CBOR_MAPPER : ObjectMappers.JSON_MAPPER; return context .getDevice() .getConfigs("encAlg", "secureKey") - .flatMap(configs -> { - Ciphers ciphers = configs.getValue("encAlg").map(Value::asString).flatMap(Ciphers::of).orElse(Ciphers.AES); + .flatMapMany(configs -> { + Ciphers ciphers = configs + .getValue("encAlg") + .map(Value::asString) + .flatMap(Ciphers::of) + .orElse(Ciphers.AES); String secureKey = configs.getValue("secureKey").map(Value::asString).orElse(null); ByteBuf byteBuf = message.getPayload(); byte[] req = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(req); byteBuf.resetReaderIndex(); - String payload = new String(ciphers.decrypt(req, secureKey)); + byte[] payload = ciphers.decrypt(req, secureKey); //解码 - return Mono.just(decode(path, decode(payload)).getMessage()); + return TopicMessageCodec.decode(objectMapper, TopicMessageCodec.removeProductPath(path), payload); }); } - protected Mono decode(CoapExchangeMessage message, MessageDecodeContext context) { + protected Flux decode(CoapExchangeMessage message, MessageDecodeContext context) { CoapExchange exchange = message.getExchange(); - return decode((CoapMessage) message, context) - .doOnSuccess(msg -> { + return decode(((CoapMessage) message), context) + .doOnComplete(() -> { exchange.respond(CoAP.ResponseCode.CREATED); exchange.accept(); }) @@ -63,8 +73,8 @@ public class JetLinksCoapDeviceMessageCodec extends JetlinksTopicMessageCodec im @Nonnull @Override - public Mono decode(@Nonnull MessageDecodeContext context) { - return Mono.defer(() -> { + public Flux decode(@Nonnull MessageDecodeContext context) { + return Flux.defer(() -> { log.debug("handle coap message:\n{}", context.getMessage()); if (context.getMessage() instanceof CoapExchangeMessage) { return decode(((CoapExchangeMessage) context.getMessage()), context); @@ -73,7 +83,7 @@ public class JetLinksCoapDeviceMessageCodec extends JetlinksTopicMessageCodec im return decode(((CoapMessage) context.getMessage()), context); } - return Mono.empty(); + return Flux.empty(); }); } diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java index 68eac18..16f263a 100644 --- a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java +++ b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java @@ -1,17 +1,16 @@ package org.jetlinks.protocol.official; 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.message.DeviceMessage; import org.jetlinks.core.message.Message; import org.jetlinks.core.message.codec.*; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import javax.annotation.Nonnull; -import java.nio.charset.StandardCharsets; /** *
@@ -49,10 +48,9 @@ import java.nio.charset.StandardCharsets;
  * @since 1.0.0
  */
 @Slf4j
-public class JetLinksMqttDeviceMessageCodec extends JetlinksTopicMessageCodec implements DeviceMessageCodec {
-
-    private Transport transport;
+public class JetLinksMqttDeviceMessageCodec implements DeviceMessageCodec {
 
+    private final Transport transport;
 
     public JetLinksMqttDeviceMessageCodec(Transport transport) {
         this.transport = transport;
@@ -74,18 +72,20 @@ public class JetLinksMqttDeviceMessageCodec extends JetlinksTopicMessageCodec im
             if (message instanceof DeviceMessage) {
                 DeviceMessage deviceMessage = ((DeviceMessage) message);
 
-                EncodedTopic convertResult = encode(deviceMessage.getDeviceId(), deviceMessage);
+                TopicPayload convertResult = TopicMessageCodec.encode(ObjectMappers.JSON_MAPPER, deviceMessage);
                 if (convertResult == null) {
                     return Mono.empty();
                 }
-                return context.getDevice()
+                return context
+                        .getDevice()
                         .getConfig(DeviceConfigKey.productId)
                         .defaultIfEmpty("null")
-                        .map(productId -> SimpleMqttMessage.builder()
+                        .map(productId -> SimpleMqttMessage
+                                .builder()
                                 .clientId(deviceMessage.getDeviceId())
-                                .topic("/" .concat(productId).concat(convertResult.topic))
+                                .topic("/".concat(productId).concat(convertResult.getTopic()))
                                 .payloadType(MessagePayloadType.JSON)
-                                .payload(Unpooled.wrappedBuffer(JSON.toJSONBytes(convertResult.payload)))
+                                .payload(Unpooled.wrappedBuffer(JSON.toJSONBytes(convertResult.getPayload())))
                                 .build());
             } else {
                 return Mono.empty();
@@ -95,18 +95,14 @@ public class JetLinksMqttDeviceMessageCodec extends JetlinksTopicMessageCodec im
 
     @Nonnull
     @Override
-    public Mono decode(@Nonnull MessageDecodeContext context) {
-        return Mono.fromSupplier(() -> {
-            MqttMessage message = (MqttMessage) context.getMessage();
-            String topic = message.getTopic();
-            String jsonData = message.getPayload().toString(StandardCharsets.UTF_8);
+    public Flux decode(@Nonnull MessageDecodeContext context) {
+        MqttMessage message = (MqttMessage) context.getMessage();
+
+        byte[] payload = message.payloadAsBytes();
+
+        return TopicMessageCodec
+                .decode(ObjectMappers.JSON_MAPPER, TopicMessageCodec.removeProductPath(message.getTopic()), payload);
 
-            JSONObject object = JSON.parseObject(jsonData, JSONObject.class);
-            if (object == null) {
-                throw new UnsupportedOperationException("cannot parse payload:{}" + jsonData);
-            }
-            return decode(topic, object).getMessage();
-        });
     }
 
 }
diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksProtocolSupportProvider.java b/src/main/java/org/jetlinks/protocol/official/JetLinksProtocolSupportProvider.java
index a885f52..8769b0b 100644
--- a/src/main/java/org/jetlinks/protocol/official/JetLinksProtocolSupportProvider.java
+++ b/src/main/java/org/jetlinks/protocol/official/JetLinksProtocolSupportProvider.java
@@ -46,9 +46,9 @@ public class JetLinksProtocolSupportProvider implements ProtocolSupportProvider
         return Mono.defer(() -> {
             CompositeProtocolSupport support = new CompositeProtocolSupport();
 
-            support.setId("jetlinks.v1.0");
-            support.setName("JetLinks V1.0");
-            support.setDescription("JetLinks Protocol Version 1.0");
+            support.setId("jetlinks.v2.0");
+            support.setName("JetLinks V2.0");
+            support.setDescription("JetLinks Protocol Version 2.0");
 
             support.addAuthenticator(DefaultTransport.MQTT, new JetLinksAuthenticator());
             support.addAuthenticator(DefaultTransport.MQTT_TLS, new JetLinksAuthenticator());
diff --git a/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java
deleted file mode 100644
index 744a793..0000000
--- a/src/main/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodec.java
+++ /dev/null
@@ -1,289 +0,0 @@
-package org.jetlinks.protocol.official;
-
-import com.alibaba.fastjson.JSONObject;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.Setter;
-import org.jetlinks.core.message.*;
-import org.jetlinks.core.message.event.EventMessage;
-import org.jetlinks.core.message.firmware.*;
-import org.jetlinks.core.message.function.FunctionInvokeMessage;
-import org.jetlinks.core.message.function.FunctionInvokeMessageReply;
-import org.jetlinks.core.message.property.*;
-import org.jetlinks.core.utils.TopicUtils;
-import org.springframework.util.Assert;
-
-import java.util.Map;
-import java.util.Optional;
-
-class JetlinksTopicMessageCodec {
-
-    @Getter
-    protected class DecodeResult {
-        private Map args;
-
-        private boolean child;
-
-        private boolean event;
-        private boolean readPropertyReply;
-        private boolean writePropertyReply;
-        private boolean functionInvokeReply;
-        private boolean reportProperties;
-        private boolean derivedMetadata;
-        private boolean register;
-        private boolean unregister;
-
-        private boolean requestFirmware;
-        private boolean reportFirmware;
-        private boolean upgradeFirmwareProgress;
-        private boolean readFirmwareReply;
-
-
-        public DecodeResult(String topic) {
-            this.topic = topic;
-            args = TopicUtils.getPathVariables("/{productId}/{deviceId}/**", topic);
-            if (topic.contains("child")) {
-                child = true;
-                args.putAll(TopicUtils.getPathVariables("/**/child/{childDeviceId}/**", topic));
-            }
-            if (topic.contains("event")) {
-                event = true;
-                args.putAll(TopicUtils.getPathVariables("/**/event/{eventId}", topic));
-            }
-            derivedMetadata = topic.endsWith("metadata/derived");
-            if (event) {
-            } else if (reportProperties = topic.endsWith("properties/report")) {
-            } else if (unregister = topic.endsWith("unregister")) {
-            } else if (register = topic.endsWith("register")) {
-            } else if (readPropertyReply = topic.endsWith("properties/read/reply")) {
-            } else if (writePropertyReply = topic.endsWith("properties/write/reply")) {
-            } else if (functionInvokeReply = topic.endsWith("function/invoke/reply")) {
-            } else if (upgradeFirmwareProgress = topic.endsWith("firmware/upgrade/progress")) {
-            } else if (requestFirmware = topic.endsWith("firmware/pull")) {
-            } else if (reportFirmware = topic.endsWith("firmware/report")) {
-            } else if (readFirmwareReply = topic.endsWith("firmware/read/reply")) {
-            } else if (derivedMetadata = topic.endsWith("metadata/derived")) {
-            }
-        }
-
-        private final String topic;
-
-        public String getDeviceId() {
-            return args.get("deviceId");
-        }
-
-        public String getChildDeviceId() {
-            return args.get("childDeviceId");
-        }
-
-        protected Message message;
-    }
-
-    protected EncodedTopic encode(String deviceId, Message message) {
-
-        Assert.hasText(deviceId, "deviceId can not be null");
-        Assert.notNull(message, "message can not be null");
-
-        if (message instanceof ReadPropertyMessage) {
-            String topic = "/".concat(deviceId).concat("/properties/read");
-            JSONObject mqttData = new JSONObject();
-            mqttData.put("messageId", message.getMessageId());
-            mqttData.put("properties", ((ReadPropertyMessage) message).getProperties());
-            mqttData.put("deviceId", deviceId);
-
-            return new EncodedTopic(topic, mqttData);
-        } else if (message instanceof WritePropertyMessage) {
-            String topic = "/".concat(deviceId).concat("/properties/write");
-            JSONObject mqttData = new JSONObject();
-            mqttData.put("messageId", message.getMessageId());
-            mqttData.put("properties", ((WritePropertyMessage) message).getProperties());
-            mqttData.put("deviceId", deviceId);
-
-            return new EncodedTopic(topic, mqttData);
-        } else if (message instanceof FunctionInvokeMessage) {
-            String topic = "/".concat(deviceId).concat("/function/invoke");
-            FunctionInvokeMessage invokeMessage = ((FunctionInvokeMessage) message);
-            JSONObject mqttData = new JSONObject();
-            mqttData.put("messageId", message.getMessageId());
-            mqttData.put("function", invokeMessage.getFunctionId());
-            mqttData.put("inputs", invokeMessage.getInputs());
-            mqttData.put("deviceId", deviceId);
-
-            return new EncodedTopic(topic, mqttData);
-        } else if (message instanceof UpgradeFirmwareMessage) {
-            String topic = "/".concat(deviceId).concat("/firmware/upgrade");
-            UpgradeFirmwareMessage firmwareMessage = ((UpgradeFirmwareMessage) message);
-            JSONObject mqttData = new JSONObject();
-            mqttData.put("messageId", message.getMessageId());
-            mqttData.put("url", firmwareMessage.getUrl());
-            mqttData.put("sign", firmwareMessage.getSign());
-            mqttData.put("version", firmwareMessage.getVersion());
-            mqttData.put("signMethod", firmwareMessage.getSignMethod());
-            mqttData.put("parameters", firmwareMessage.getParameters());
-            mqttData.put("deviceId", deviceId);
-
-            return new EncodedTopic(topic, mqttData);
-        } else if (message instanceof ReadFirmwareMessage) {
-            String topic = "/".concat(deviceId).concat("/firmware/read");
-            JSONObject mqttData = new JSONObject();
-            mqttData.put("messageId", message.getMessageId());
-            mqttData.put("deviceId", deviceId);
-            return new EncodedTopic(topic, mqttData);
-        } else if (message instanceof RequestFirmwareMessageReply) {
-            String topic = "/".concat(deviceId).concat("/firmware/pull/reply");
-            RequestFirmwareMessageReply firmwareMessage = ((RequestFirmwareMessageReply) message);
-            JSONObject mqttData = new JSONObject();
-            mqttData.put("messageId", message.getMessageId());
-            mqttData.put("url", firmwareMessage.getUrl());
-            mqttData.put("sign", firmwareMessage.getSign());
-            mqttData.put("version", firmwareMessage.getVersion());
-            mqttData.put("signMethod", firmwareMessage.getSignMethod());
-            mqttData.put("parameters", firmwareMessage.getParameters());
-            mqttData.put("deviceId", deviceId);
-            return new EncodedTopic(topic, mqttData);
-        } else if (message instanceof ChildDeviceMessage) {
-            ChildDeviceMessage childDeviceMessage = ((ChildDeviceMessage) message);
-            EncodedTopic result = encode(childDeviceMessage.getChildDeviceId(), childDeviceMessage.getChildDeviceMessage());
-            String topic = "/".concat(deviceId).concat("/child").concat(result.topic);
-            result.payload.put("deviceId", childDeviceMessage.getChildDeviceId());
-
-            return new EncodedTopic(topic, result.payload);
-        }
-        return null;
-    }
-
-    protected DecodeResult decode(String topic, JSONObject object) {
-        DecodeResult result = new DecodeResult(topic);
-        Message message = null;
-        if (result.isEvent()) {
-            message = decodeEvent(result, object);
-        } else if (result.isReportProperties()) {
-            message = decodeReportPropertyReply(result, object);
-        } else if (result.isReadPropertyReply()) {
-            message = decodeReadPropertyReply(result, object);
-        } else if (result.isWritePropertyReply()) {
-            message = decodeWritePropertyReply(result, object);
-        } else if (result.isFunctionInvokeReply()) {
-            message = decodeInvokeReply(result, object);
-        } else if (result.isRegister()) {
-            message = decodeRegister(result, object);
-        } else if (result.isUnregister()) {
-            message = decodeUnregister(result, object);
-        } else if (result.isDerivedMetadata()) {
-            message = decodeDerivedMetadata(result, object);
-        } else if (result.isReadFirmwareReply()) {
-            message = object.toJavaObject(ReadFirmwareMessageReply.class);
-        } else if (result.isRequestFirmware()) {
-            message = object.toJavaObject(RequestFirmwareMessage.class);
-        } else if (result.isReportFirmware()) {
-            message = object.toJavaObject(ReportFirmwareMessage.class);
-        } else if (result.isUpgradeFirmwareProgress()) {
-            message = object.toJavaObject(UpgradeFirmwareProgressMessage.class);
-        } else if (topic.endsWith("connected")) {
-            message = object.toJavaObject(DeviceOnlineMessage.class);
-        } else if (topic.endsWith("disconnect")) {
-            message = object.toJavaObject(DeviceOfflineMessage.class);
-        }
-
-        if (result.isChild()) {
-            if (message == null) {
-                throw new UnsupportedOperationException("unsupported topic:" + topic);
-            }
-            applyCommons(message, result, object);
-            ChildDeviceMessage children = new ChildDeviceMessage();
-            children.setChildDeviceId(result.getChildDeviceId());
-            children.setDeviceId(result.getDeviceId());
-            children.setChildDeviceMessage(message);
-            children.setTimestamp(Optional.ofNullable(object.getLong("timestamp")).orElse(System.currentTimeMillis()));
-            Optional.ofNullable(object.getString("messageId")).ifPresent(children::setMessageId);
-            result.message = children;
-        } else {
-            if (message == null) {
-                throw new UnsupportedOperationException("unsupported topic:" + topic);
-            }
-            applyCommons(message, result, object);
-            result.message = message;
-        }
-        return result;
-    }
-
-
-    private Message decodeEvent(DecodeResult result, JSONObject event) {
-        EventMessage message = event.toJavaObject(EventMessage.class);
-        message.setData(event.get("data"));
-        message.setEvent(result.args.get("eventId"));
-        return message;
-    }
-
-    private Message decodeReadPropertyReply(DecodeResult result, JSONObject data) {
-
-        return data.toJavaObject(ReadPropertyMessageReply.class);
-    }
-
-
-    private Message decodeReportPropertyReply(DecodeResult result, JSONObject data) {
-
-        return data.toJavaObject(ReportPropertyMessage.class);
-    }
-
-
-    private Message decodeWritePropertyReply(DecodeResult result, JSONObject data) {
-
-        return data.toJavaObject(WritePropertyMessageReply.class);
-    }
-
-    private Message decodeInvokeReply(DecodeResult result, JSONObject data) {
-        return data.toJavaObject(FunctionInvokeMessageReply.class);
-    }
-
-    private Message decodeRegister(DecodeResult result, JSONObject data) {
-        return data.toJavaObject(DeviceRegisterMessage.class);
-    }
-
-    private Message decodeUnregister(DecodeResult result, JSONObject data) {
-        return data.toJavaObject(DeviceUnRegisterMessage.class);
-    }
-
-    private Message decodeDerivedMetadata(DecodeResult result, JSONObject data) {
-        return data.toJavaObject(DerivedMetadataMessage.class);
-    }
-
-    private void applyCommons(Message message, DecodeResult result, JSONObject data) {
-        if (message instanceof CommonDeviceMessageReply) {
-            CommonDeviceMessageReply reply = ((CommonDeviceMessageReply) message);
-            reply.setSuccess(Optional.ofNullable(data.getBoolean("success")).orElse(true));
-            reply.setTimestamp(Optional.ofNullable(data.getLong("timestamp")).orElse(System.currentTimeMillis()));
-            if (result.isChild()) {
-                reply.setDeviceId(result.getChildDeviceId());
-            } else {
-                reply.setDeviceId(result.getDeviceId());
-            }
-        }
-        if (message instanceof CommonDeviceMessage) {
-            CommonDeviceMessage msg = ((CommonDeviceMessage) message);
-            msg.setTimestamp(Optional.ofNullable(data.getLong("timestamp")).orElse(System.currentTimeMillis()));
-            if (result.isChild()) {
-                msg.setDeviceId(result.getChildDeviceId());
-            } else {
-                msg.setDeviceId(result.getDeviceId());
-            }
-        }
-    }
-
-    @Getter
-    @Setter
-    @AllArgsConstructor
-    protected class EncodedTopic {
-        String topic;
-
-        JSONObject payload;
-    }
-
-    @Getter
-    @Setter
-    protected class Decoded {
-        Message message;
-
-    }
-
-}
diff --git a/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java b/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java
new file mode 100644
index 0000000..006fdc9
--- /dev/null
+++ b/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java
@@ -0,0 +1,23 @@
+package org.jetlinks.protocol.official;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+public class ObjectMappers {
+
+    public static final ObjectMapper JSON_MAPPER;
+    public static final ObjectMapper CBOR_MAPPER;
+
+    static {
+        JSON_MAPPER = Jackson2ObjectMapperBuilder
+                .json()
+                .build()
+                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        CBOR_MAPPER = Jackson2ObjectMapperBuilder
+                .cbor()
+                .build()
+                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+    }
+
+}
diff --git a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
new file mode 100644
index 0000000..d3a9f6a
--- /dev/null
+++ b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
@@ -0,0 +1,191 @@
+package org.jetlinks.protocol.official;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.SneakyThrows;
+import org.hswebframework.web.bean.FastBeanCopier;
+import org.jetlinks.core.message.*;
+import org.jetlinks.core.message.event.EventMessage;
+import org.jetlinks.core.message.firmware.*;
+import org.jetlinks.core.message.function.FunctionInvokeMessage;
+import org.jetlinks.core.message.function.FunctionInvokeMessageReply;
+import org.jetlinks.core.message.property.*;
+import org.jetlinks.core.utils.TopicUtils;
+import org.reactivestreams.Publisher;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+
+public enum TopicMessageCodec {
+    //上报属性数据
+    reportProperty("/*/properties/report", ReportPropertyMessage.class),
+    //事件上报
+    event("/*/event/*", EventMessage.class),
+    //读取属性
+    readProperty("/*/properties/read", ReadPropertyMessage.class),
+    //读取属性回复
+    readPropertyReply("/*/properties/read/reply", ReadPropertyMessageReply.class),
+    //修改属性
+    writeProperty("/*/properties/write", WritePropertyMessage.class),
+    //修改属性回复
+    writePropertyReply("/*/properties/write/reply", WritePropertyMessageReply.class),
+    //调用功能
+    functionInvoke("/*/function/invoke", FunctionInvokeMessage.class),
+    //调用功能回复
+    functionInvokeReply("/*/function/invoke/reply", FunctionInvokeMessageReply.class),
+    //子设备消息
+    child("/*/child/*/**", ChildDeviceMessage.class) {
+        @Override
+        public Publisher doDecode(ObjectMapper mapper, String[] topic, byte[] payload) {
+            String[] _topic = Arrays.copyOfRange(topic, 2, topic.length);
+            _topic[0] = "";// topic以/开头所有第一位是空白
+            return TopicMessageCodec
+                    .decode(mapper, _topic, payload)
+                    .map(childMsg -> {
+                        ChildDeviceMessage msg = new ChildDeviceMessage();
+                        msg.setDeviceId(topic[1]);
+                        msg.setChildDeviceMessage(childMsg);
+                        msg.setTimestamp(childMsg.getTimestamp());
+                        msg.setMessageId(childMsg.getMessageId());
+                        return msg;
+                    });
+        }
+
+        @Override
+        protected TopicPayload doEncode(ObjectMapper mapper, String[] topics, DeviceMessage message) {
+            ChildDeviceMessage deviceMessage = ((ChildDeviceMessage) message);
+
+            DeviceMessage childMessage = ((DeviceMessage) deviceMessage.getChildDeviceMessage());
+
+            TopicPayload payload = TopicMessageCodec.encode(mapper, childMessage);
+            String[] childTopic = payload.getTopic().split("/");
+            String[] topic = new String[topics.length + childTopic.length - 3];
+            //合并topic
+            System.arraycopy(topics, 0, topic, 0, topics.length - 1);
+            System.arraycopy(childTopic, 1, topic, topics.length - 2, childTopic.length - 1);
+
+            refactorTopic(topic, message);
+            payload.setTopic(String.join("/", topic));
+            return payload;
+
+        }
+    },
+    //更新标签
+    updateTag("/*/tags", UpdateTagMessage.class),
+    //注册
+    register("/*/register", DeviceRegisterMessage.class),
+    //注销
+    unregister("/*/unregister", DeviceUnRegisterMessage.class),
+    //更新固件消息
+    upgradeFirmware("/*/firmware/upgrade", UpgradeFirmwareMessage.class),
+    //更新固件升级进度消息
+    upgradeProcessFirmware("/*/firmware/upgrade/progress", UpgradeFirmwareProgressMessage.class),
+    //拉取固件
+    requestFirmware("/*/firmware/pull", RequestFirmwareMessage.class),
+    //上报固件版本
+    reportFirmware("/*/firmware/report", ReportFirmwareMessage.class),
+    //读取固件回复
+    readFirmwareReply("/*/firmware/read/reply", ReadFirmwareMessageReply.class),
+    //派生物模型上报
+    derivedMetadata("/*/metadata/derived", DerivedMetadataMessage.class),
+    //透传设备消息
+    direct("/*/direct", DirectDeviceMessage.class) {
+        @Override
+        public Publisher doDecode(ObjectMapper mapper, String[] topic, byte[] payload) {
+            DirectDeviceMessage message = new DirectDeviceMessage();
+            message.setDeviceId(topic[1]);
+            message.setPayload(payload);
+            return Mono.just(message);
+        }
+    },
+
+    ;
+
+    TopicMessageCodec(String topic, Class type) {
+        this.pattern = topic.split("/");
+        this.type = type;
+    }
+
+    private final String[] pattern;
+    private final Class type;
+
+    public static Flux decode(ObjectMapper mapper, String[] topics, byte[] payload) {
+        return Mono
+                .justOrEmpty(fromTopic(topics))
+                .flatMapMany(topicMessageCodec -> topicMessageCodec.doDecode(mapper, topics, payload));
+    }
+
+    public static Flux decode(ObjectMapper mapper, String topic, byte[] payload) {
+        return decode(mapper, topic.split("/"), payload);
+    }
+
+    public static TopicPayload encode(ObjectMapper mapper, DeviceMessage message) {
+
+        return fromMessage(message)
+                .orElseThrow(() -> new UnsupportedOperationException("unsupported message:" + message.getMessageType()))
+                .doEncode(mapper, message);
+    }
+
+    static Optional fromTopic(String[] topic) {
+        for (TopicMessageCodec value : values()) {
+            if (TopicUtils.match(value.pattern, topic)) {
+                return Optional.of(value);
+            }
+        }
+        return Optional.empty();
+    }
+
+    static Optional fromMessage(DeviceMessage message) {
+        for (TopicMessageCodec value : values()) {
+            if (value.type == message.getClass()) {
+                return Optional.of(value);
+            }
+        }
+        return Optional.empty();
+    }
+
+    Publisher doDecode(ObjectMapper mapper, String[] topic, byte[] payload) {
+        return Mono
+                .fromCallable(() -> {
+                    DeviceMessage message = mapper.readValue(payload, type);
+                    FastBeanCopier.copy(Collections.singletonMap("deviceId", topic[1]), message);
+
+                    return message;
+                });
+    }
+
+    @SneakyThrows
+    TopicPayload doEncode(ObjectMapper mapper, String[] topics, DeviceMessage message) {
+        refactorTopic(topics, message);
+        return TopicPayload.of(String.join("/", topics), mapper.writeValueAsBytes(message));
+    }
+
+    @SneakyThrows
+    TopicPayload doEncode(ObjectMapper mapper, DeviceMessage message) {
+        String[] topics = Arrays.copyOf(pattern, pattern.length);
+        return doEncode(mapper, topics, message);
+    }
+
+    void refactorTopic(String[] topics, DeviceMessage message) {
+        topics[1] = message.getDeviceId();
+    }
+
+    /**
+     * 移除topic中的产品信息,topic第一个层为产品ID,在解码时,不需要此信息,所以需要移除之.
+     *
+     * @param topic topic
+     * @return 移除后的topic
+     */
+    public static String[] removeProductPath(String topic) {
+        if (!topic.startsWith("/")) {
+            topic = "/" + topic;
+        }
+        String[] topicArr = topic.split("/");
+        String[] topics = Arrays.copyOfRange(topicArr, 1, topicArr.length);
+        topics[0] = "";
+        return topics;
+    }
+
+}
diff --git a/src/main/java/org/jetlinks/protocol/official/TopicPayload.java b/src/main/java/org/jetlinks/protocol/official/TopicPayload.java
new file mode 100644
index 0000000..7cc4956
--- /dev/null
+++ b/src/main/java/org/jetlinks/protocol/official/TopicPayload.java
@@ -0,0 +1,15 @@
+package org.jetlinks.protocol.official;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor(staticName = "of") // FIXME 使用对象池,节省一丢丢内存?
+public class TopicPayload {
+
+    private String topic;
+
+    private byte[] payload;
+}
diff --git a/src/main/resources/services/org.jetlinks.core.spi.ProtocolSupportProvider b/src/main/resources/services/org.jetlinks.core.spi.ProtocolSupportProvider
deleted file mode 100644
index 39e2abe..0000000
--- a/src/main/resources/services/org.jetlinks.core.spi.ProtocolSupportProvider
+++ /dev/null
@@ -1 +0,0 @@
-org.jetlinks.protocol.official.JetLinksProtocolSupportProvider
\ No newline at end of file
diff --git a/src/test/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodecTest.java b/src/test/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodecTest.java
index 00ec80e..815e115 100644
--- a/src/test/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodecTest.java
+++ b/src/test/java/org/jetlinks/protocol/official/JetLinksCoapDeviceMessageCodecTest.java
@@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
 public class JetLinksCoapDeviceMessageCodecTest {
 
 
-   JetLinksCoapDeviceMessageCodec codec = new JetLinksCoapDeviceMessageCodec();
+    JetLinksCoapDeviceMessageCodec codec = new JetLinksCoapDeviceMessageCodec();
 
     DeviceOperator device;
 
@@ -39,10 +39,11 @@ public class JetLinksCoapDeviceMessageCodecTest {
     @Before
     public void init() {
         TestDeviceRegistry registry = new TestDeviceRegistry(new CompositeProtocolSupports(), new StandaloneDeviceMessageBroker());
-        device = registry.register(DeviceInfo.builder()
-                .id("test")
-                .protocol("jetlinks")
-                .build())
+        device = registry
+                .register(DeviceInfo.builder()
+                                    .id("test")
+                                    .protocol("jetlinks")
+                                    .build())
                 .flatMap(operator -> operator.setConfig("secureKey", key).thenReturn(operator))
                 .block();
     }
@@ -59,19 +60,20 @@ public class JetLinksCoapDeviceMessageCodecTest {
 
                     @Override
                     public void handlePOST(CoapExchange exchange) {
-                        codec.decode(new MessageDecodeContext() {
-                            @Nonnull
-                            @Override
-                            public EncodedMessage getMessage() {
-                                return new CoapExchangeMessage(exchange);
-                            }
+                        codec
+                                .decode(new MessageDecodeContext() {
+                                    @Nonnull
+                                    @Override
+                                    public EncodedMessage getMessage() {
+                                        return new CoapExchangeMessage(exchange);
+                                    }
 
-                            @Override
-                            public DeviceOperator getDevice() {
-                                return device;
-                            }
-                        })
-                                .doOnSuccess(messageRef::set)
+                                    @Override
+                                    public DeviceOperator getDevice() {
+                                        return device;
+                                    }
+                                })
+                                .doOnNext(messageRef::set)
                                 .doOnError(Throwable::printStackTrace)
                                 .subscribe();
                     }
@@ -85,7 +87,7 @@ public class JetLinksCoapDeviceMessageCodecTest {
         };
 
         Endpoint endpoint = new CoapEndpoint.Builder()
-                .setPort(12345).build();
+                .setPort(12341).build();
         server.addEndpoint(endpoint);
         server.start();
 
@@ -96,8 +98,8 @@ public class JetLinksCoapDeviceMessageCodecTest {
         Request request = Request.newPost();
         String payload = "{\"data\":1}";
 
-        request.setURI("coap://localhost:12345/test/test/event/event1");
-        request.setPayload(Ciphers.AES.encrypt(payload.getBytes(),key));
+        request.setURI("coap://localhost:12341/test/test/event/event1");
+        request.setPayload(Ciphers.AES.encrypt(payload.getBytes(), key));
 //        request.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);
 
         CoapResponse response = coapClient.advanced(request);
diff --git a/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java b/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java
index ea3314b..44d60fd 100644
--- a/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java
+++ b/src/test/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodecTest.java
@@ -7,7 +7,6 @@ import org.jetlinks.core.device.DeviceOperator;
 import org.jetlinks.core.device.ProductInfo;
 import org.jetlinks.core.device.StandaloneDeviceMessageBroker;
 import org.jetlinks.core.message.ChildDeviceMessage;
-import org.jetlinks.core.message.ChildDeviceMessageReply;
 import org.jetlinks.core.message.DerivedMetadataMessage;
 import org.jetlinks.core.message.Message;
 import org.jetlinks.core.message.codec.*;
@@ -16,7 +15,6 @@ import org.jetlinks.core.message.firmware.UpgradeFirmwareMessage;
 import org.jetlinks.core.message.function.FunctionInvokeMessage;
 import org.jetlinks.core.message.function.FunctionInvokeMessageReply;
 import org.jetlinks.core.message.property.*;
-import org.jetlinks.supports.official.JetLinksMqttDeviceMessageCodec;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -28,7 +26,7 @@ import java.util.Collections;
 
 public class JetLinksMqttDeviceMessageCodecTest {
 
-    org.jetlinks.supports.official.JetLinksMqttDeviceMessageCodec codec = new JetLinksMqttDeviceMessageCodec();
+    JetLinksMqttDeviceMessageCodec codec = new JetLinksMqttDeviceMessageCodec();
 
 
     TestDeviceRegistry registry;
@@ -87,7 +85,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/properties/read/reply")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ReadPropertyMessageReply);
         ReadPropertyMessageReply reply = ((ReadPropertyMessageReply) message);
@@ -103,12 +101,11 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/child/test/properties/read/reply")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
-        Assert.assertTrue(message instanceof ChildDeviceMessageReply);
-        ChildDeviceMessageReply childReply = ((ChildDeviceMessageReply) message);
+        Assert.assertTrue(message instanceof ChildDeviceMessage);
+        ChildDeviceMessage childReply = ((ChildDeviceMessage) message);
 
-        Assert.assertTrue(childReply.isSuccess());
         Assert.assertEquals(childReply.getDeviceId(),"device1");
         Assert.assertEquals(childReply.getMessageId(),"test");
 
@@ -151,7 +148,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
 
 
         Assert.assertNotNull(encodedMessage);
-        Assert.assertEquals(encodedMessage.getTopic(), "/product1/device1/child/test/properties/write");
+        Assert.assertEquals(encodedMessage.getTopic(), "/product1/device1/child/device1/properties/write");
         System.out.println(encodedMessage.getPayload().toString(StandardCharsets.UTF_8));
     }
 
@@ -160,7 +157,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/properties/write/reply")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof WritePropertyMessageReply);
         WritePropertyMessageReply reply = ((WritePropertyMessageReply) message);
@@ -178,7 +175,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/child/test/properties/write/reply")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ChildDeviceMessage);
         ChildDeviceMessage childReply = ((ChildDeviceMessage) message);
@@ -232,7 +229,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/function/invoke/reply")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"output\":\"ok\"}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof FunctionInvokeMessageReply);
         FunctionInvokeMessageReply reply = ((FunctionInvokeMessageReply) message);
@@ -248,7 +245,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/child/test/function/invoke/reply")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"output\":\"ok\"}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ChildDeviceMessage);
         ChildDeviceMessage childReply = ((ChildDeviceMessage) message);
@@ -269,7 +266,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/event/temp")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"data\":100}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof EventMessage);
         EventMessage reply = ((EventMessage) message);
@@ -284,7 +281,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/child/test/event/temp")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"data\":100}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ChildDeviceMessage);
 
@@ -300,7 +297,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/properties/report")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ReportPropertyMessage);
         ReportPropertyMessage reply = ((ReportPropertyMessage) message);
@@ -316,7 +313,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/child/test/properties/report")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"properties\":{\"sn\":\"test\"}}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ChildDeviceMessage);
 
@@ -333,7 +330,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/metadata/derived")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"metadata\":\"1\"}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof DerivedMetadataMessage);
         DerivedMetadataMessage reply = ((DerivedMetadataMessage) message);
@@ -348,7 +345,7 @@ public class JetLinksMqttDeviceMessageCodecTest {
         Message message = codec.decode(createMessageContext(SimpleMqttMessage.builder()
                 .topic("/product1/device1/child/test/metadata/derived")
                 .payload(Unpooled.wrappedBuffer("{\"messageId\":\"test\",\"metadata\":\"1\"}".getBytes()))
-                .build())).block();
+                .build())).blockFirst();
 
         Assert.assertTrue(message instanceof ChildDeviceMessage);
 
diff --git a/src/test/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodecTest.java b/src/test/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodecTest.java
deleted file mode 100644
index e34950c..0000000
--- a/src/test/java/org/jetlinks/protocol/official/JetlinksTopicMessageCodecTest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.jetlinks.protocol.official;
-
-import org.jetlinks.core.message.property.ReadPropertyMessage;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.util.Arrays;
-
-public class JetlinksTopicMessageCodecTest {
-
-    JetlinksTopicMessageCodec codec = new  JetlinksTopicMessageCodec();
-
-    @Test
-    public void testReadProperty() {
-
-        ReadPropertyMessage readProperty = new ReadPropertyMessage();
-        readProperty.setProperties(Arrays.asList("name"));
-        readProperty.setMessageId("test");
-        JetlinksTopicMessageCodec.EncodedTopic topic = codec.encode("test", readProperty);
-        Assert.assertEquals(topic.getTopic(),"/test/properties/read");
-
-    }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/jetlinks/protocol/official/TopicMessageCodecTest.java b/src/test/java/org/jetlinks/protocol/official/TopicMessageCodecTest.java
new file mode 100644
index 0000000..3d34fe0
--- /dev/null
+++ b/src/test/java/org/jetlinks/protocol/official/TopicMessageCodecTest.java
@@ -0,0 +1,44 @@
+package org.jetlinks.protocol.official;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.jetlinks.core.message.ChildDeviceMessage;
+import org.jetlinks.core.message.property.ReportPropertyMessage;
+import org.junit.Test;
+import reactor.test.StepVerifier;
+
+import static org.junit.Assert.*;
+
+public class TopicMessageCodecTest {
+
+
+    public void testChild(ObjectMapper objectMapper) {
+        ChildDeviceMessage message = new ChildDeviceMessage();
+        message.setDeviceId("test");
+        ReportPropertyMessage msg = new ReportPropertyMessage();
+        msg.setDeviceId("childId");
+        message.setChildDeviceMessage(msg);
+        message.setTimestamp(msg.getTimestamp());
+
+
+        TopicPayload payload = TopicMessageCodec.child.doEncode(objectMapper, message);
+        System.out.println(payload.getPayload().length);
+        assertEquals("/test/child/childId/properties/report", payload.getTopic());
+
+        TopicMessageCodec
+                .decode(objectMapper, payload.getTopic(), payload.getPayload())
+                .as(StepVerifier::create)
+                .expectNextMatches(deviceMessage -> {
+                    System.out.println(message);
+                    System.out.println(deviceMessage);
+                    return deviceMessage.toJson().equals(message.toJson());
+                })
+                .verifyComplete();
+
+    }
+
+    @Test
+    public void doTest() {
+        testChild(ObjectMappers.JSON_MAPPER);
+        testChild(ObjectMappers.CBOR_MAPPER);
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644
index 0000000..74df945
--- /dev/null
+++ b/src/test/resources/logback.xml
@@ -0,0 +1,15 @@
+
+
+    
+    
+     
+    
+        
+            %-4relative [%thread] %-5level %logger{35} - %msg %n
+        
+    
+
+    
+        
+    
+
\ No newline at end of file

From 5ba0238f8b1b9cb9afdd60e14f3a8b5aa8961913 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Thu, 21 Jan 2021 17:00:52 +0800
Subject: [PATCH 03/15] deploy

---
 pom.xml | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/pom.xml b/pom.xml
index 4300c9e..bd112df 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,6 +50,80 @@
         Dysprosium-RELEASE
     
 
+    
+        
+            release
+            
+                
+                    
+                        org.sonatype.plugins
+                        nexus-staging-maven-plugin
+                        1.6.3
+                        true
+                        
+                            sonatype-releases
+                            https://oss.sonatype.org/
+                            true
+                            120
+                        
+                    
+                    
+                        org.apache.maven.plugins
+                        maven-release-plugin
+                        
+                            true
+                            false
+                            release
+                            deploy
+                        
+                    
+                    
+                        org.apache.maven.plugins
+                        maven-gpg-plugin
+                        1.5
+                        
+                            
+                                sign-artifacts
+                                verify
+                                
+                                    sign
+                                
+                            
+                        
+                    
+                    
+                        org.apache.maven.plugins
+                        maven-javadoc-plugin
+                        2.9.1
+                        
+                            -Xdoclint:none
+                        
+                        
+                            
+                                attach-javadocs
+                                
+                                    jar
+                                
+                            
+                        
+                    
+                
+            
+            
+                
+                    sonatype-releases
+                    sonatype repository
+                    https://oss.sonatype.org/service/local/staging/deploy/maven2
+                
+                
+                    sonatype-snapshots
+                    Nexus Snapshot Repository
+                    https://oss.sonatype.org/content/repositories/snapshots
+                
+            
+        
+    
+
     
         
             
@@ -126,6 +200,20 @@
         
     
 
+    
+        
+            releases
+            Nexus Release Repository
+            http://nexus.hsweb.me/content/repositories/releases/
+        
+        
+            snapshots
+            Nexus Snapshot Repository
+            http://nexus.hsweb.me/content/repositories/snapshots/
+        
+    
+
+
     
         
             hsweb-nexus

From 53b349efee7b596a1f6e6218f0f6b828a7044a98 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Thu, 21 Jan 2021 17:01:14 +0800
Subject: [PATCH 04/15] 2.0

---
 .../jetlinks-official-protocol-1.0-SNAPSHOT.jar | Bin 28681 -> 0 bytes
 .../jetlinks-official-protocol-2.0-SNAPSHOT.jar | Bin 0 -> 30393 bytes
 2 files changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 package/jetlinks-official-protocol-1.0-SNAPSHOT.jar
 create mode 100644 package/jetlinks-official-protocol-2.0-SNAPSHOT.jar

diff --git a/package/jetlinks-official-protocol-1.0-SNAPSHOT.jar b/package/jetlinks-official-protocol-1.0-SNAPSHOT.jar
deleted file mode 100644
index 90321664dc09d24cb85d1c4ebac6764026c3fe3d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 28681
zcmbrl1C%Dgx+PlZvi+BB+qP|W**3enY}>YN+eVk`FLv3ketpk5GjGnknYnk?i?uQ{
za^)A9fiE)l-VvcB3;qoju1SSynRV~TnNDVQoKBWr
z1RCnFSR)yewx7(iBU^^mMaDIUrUt!|!^J4yvjRz-Wf9L&wFOstIlPl+l1^TZ}rj+SQ5
z|3d~c{}(OkV&?2_Wn%XKmhGP{sm4R9-2b=z3y45KME~2FW9U4_pdPkxLg3xgeuq0qdhyWU)(KKy+SyCvP{{*{toAGqRj3
zvY(%RojDsBYr9#`JJkd|o=?OeQ|YL~)~-cqP
z$TTiR6z&qd;$(mcBb0yy_#gu7upWIbJw9R_sCyX6P@h2?}ixOW&vJ;KL$
zg8*?JFf`YQ2L@YMgFcRV!Ox*g_}6Gt*I3;GM5%&+{;->UWP!lARG^9RD^Q(k(21EB
zWmtNPaG07;Db^LaYyqv`C>w>=0-P&MF=`{o_>vd~2`*w5>K0mnG*yt6ATR0Wm8drW
zPUN0Z<|Q#$5<3#1DypDzrLily7)94z%BrQavweFU+0k}Ij)HE}$kGuM(P?acW=LAR
z$+mXbi>1Qd$veVX{_GTjV!E-Vvx4^=8#7cgK(jBbpg02)n?}bi$w6_1w`04&9cL>=
zNi~MQvL?#oN5e38IVCp7gGFLJ{n#R%XoE4c{*t=r#%PX4!-{(m(Dp#mHKxsr1==15
zMh_2jdd$PAj6Vf=(Dk9Bmq<^L<*1{gL*{~^x|?y-9II%91>ZmUV&2+C-Gy#*NUE3z
zf!`~BQzpwyXIN;FPbd_l$V8~tZmmmhMS6qm7=JD#-3D%n%<#n(cP&Y3rAwu>XzZ1M
zzaq(nGqd!lJk~zraIc~!Y?<`8viJ-g%_jYl95y8J?C-dC)s_`5kM`|25$p!M0#%ON
z1TM4n+Ec~U{dE^&C4qYjT)*pbKgM}I&<9e!vw6B)HTS1UCXz|;6&kUQK_y_0a?9+!
zQXWB4rU&?9wt86li1o|?3No>kIdqS7k3r(oCJN0F3O_qMMwm+3sMwXgn$zC%L
zl2gp={j|nRByDAZM`cbWs%la!h)QeA^?L6R1aUX>Wo#wBy(*eLVsSRZ{*+n<8J|Q*
z`5cb{YNf6m^uk5-SiIu7|AaDAk(x{J<}5?59CmDQ9n-i0nHVqM_F+xw#HG2YJOy`7
zQ&ng}-HtKF>R-XH#4(_3z)FVrnW*+(QaCd`!+1Su@2!W
zwAeIRbW;UMB^1BfUFIVxXeV!GWPGbc2HR+F@v(PDp2>U-N0Dy0YCh{x6~kKyCtjEd
z(i$)}O)o@vM{=U`_R5y=qW84MJy8`WUaTQqS3!d_xo7B
z!lIUk!oyQ;>0Uz`B3U%lCL+x=24&1NM#&;lrs(=bP?O9!LSxLn!{WPTsMpJ2ukCw#
z*EgWzQZ~_W7?`82i)F|Uf`*Q8$7CEX3t_)8SE5~JSiyPdf@a8@tCDHZHξC>v>x
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
w@AvNaY0fV!DVslC*MGS*|D{1cv3^Z@
Date: Tue, 26 Jan 2021 16:40:57 +0800
Subject: [PATCH 05/15] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dencode=E9=94=99?=
 =?UTF-8?q?=E8=AF=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../protocol/official/JetLinksMqttDeviceMessageCodec.java       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
index 16f263a..7be7abe 100644
--- a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
@@ -85,7 +85,7 @@ public class JetLinksMqttDeviceMessageCodec implements DeviceMessageCodec {
                                 .clientId(deviceMessage.getDeviceId())
                                 .topic("/".concat(productId).concat(convertResult.getTopic()))
                                 .payloadType(MessagePayloadType.JSON)
-                                .payload(Unpooled.wrappedBuffer(JSON.toJSONBytes(convertResult.getPayload())))
+                                .payload(Unpooled.wrappedBuffer(convertResult.getPayload()))
                                 .build());
             } else {
                 return Mono.empty();

From 36516ae5b11895fd03e1e0ae353b4b1b3a7dde3b Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Tue, 26 Jan 2021 16:41:16 +0800
Subject: [PATCH 06/15] =?UTF-8?q?=E4=B8=8D=E5=BA=8F=E5=88=97=E5=8C=96null?=
 =?UTF-8?q?=E5=AD=97=E6=AE=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../org/jetlinks/protocol/official/ObjectMappers.java    | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java b/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java
index 006fdc9..d8240af 100644
--- a/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java
+++ b/src/main/java/org/jetlinks/protocol/official/ObjectMappers.java
@@ -1,7 +1,9 @@
 package org.jetlinks.protocol.official;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
 public class ObjectMappers {
@@ -13,11 +15,14 @@ public class ObjectMappers {
         JSON_MAPPER = Jackson2ObjectMapperBuilder
                 .json()
                 .build()
-                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
+        ;
         CBOR_MAPPER = Jackson2ObjectMapperBuilder
                 .cbor()
                 .build()
-                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+                .setSerializationInclusion(JsonInclude.Include.NON_NULL);
     }
 
 }

From 08d90485bec6c85cf2e1c018ac5487475b5503bb Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Tue, 26 Jan 2021 16:42:22 +0800
Subject: [PATCH 07/15] upgrade

---
 ...etlinks-official-protocol-2.0-SNAPSHOT.jar | Bin 30393 -> 30431 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/package/jetlinks-official-protocol-2.0-SNAPSHOT.jar b/package/jetlinks-official-protocol-2.0-SNAPSHOT.jar
index 1e4b66fbb66fedde7d36c4df25ae4b92ac551181..db1a02752e8fb0b7fc3725dca81abf1194cf0444 100644
GIT binary patch
delta 4068
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

delta 3968
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
Date: Fri, 5 Mar 2021 18:37:01 +0800
Subject: [PATCH 08/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8A=E4=B8=8B?=
 =?UTF-8?q?=E7=BA=BF=E6=B6=88=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../official/JetLinksMqttDeviceMessageCodec.java         | 9 +++++++++
 .../jetlinks/protocol/official/TopicMessageCodec.java    | 7 ++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
index 7be7abe..7441d52 100644
--- a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
@@ -5,8 +5,10 @@ import io.netty.buffer.Unpooled;
 import lombok.extern.slf4j.Slf4j;
 import org.jetlinks.core.device.DeviceConfigKey;
 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 org.jetlinks.core.server.session.DeviceSession;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -69,6 +71,13 @@ public class JetLinksMqttDeviceMessageCodec implements DeviceMessageCodec {
     public Mono encode(@Nonnull MessageEncodeContext context) {
         return Mono.defer(() -> {
             Message message = context.getMessage();
+
+            if (message instanceof DisconnectDeviceMessage) {
+                return ((ToDeviceMessageContext) context)
+                        .disconnect()
+                        .then(Mono.empty());
+            }
+
             if (message instanceof DeviceMessage) {
                 DeviceMessage deviceMessage = ((DeviceMessage) message);
 
diff --git a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
index d3a9f6a..09ca53f 100644
--- a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
@@ -100,7 +100,12 @@ public enum TopicMessageCodec {
             return Mono.just(message);
         }
     },
-
+    //断开连接消息
+    disconnect("/*/disconnect", DisconnectDeviceMessage.class),
+    //上线
+    connect("/*/online", DeviceOnlineMessage.class),
+    //离线
+    offline("/*/offline", DeviceOfflineMessage.class),
     ;
 
     TopicMessageCodec(String topic, Class type) {

From 66a09c951cf301376326a3c056fcaffa1fd09ae1 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Wed, 17 Mar 2021 17:06:15 +0800
Subject: [PATCH 09/15] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AE=BE=E5=A4=87?=
 =?UTF-8?q?=E6=B6=88=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../org/jetlinks/protocol/official/TopicMessageCodec.java     | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
index 09ca53f..d26d40d 100644
--- a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
@@ -102,10 +102,14 @@ public enum TopicMessageCodec {
     },
     //断开连接消息
     disconnect("/*/disconnect", DisconnectDeviceMessage.class),
+    //断开连接回复
+    disconnectReply("/*/disconnect/reply", DisconnectDeviceMessageReply.class),
     //上线
     connect("/*/online", DeviceOnlineMessage.class),
     //离线
     offline("/*/offline", DeviceOfflineMessage.class),
+    //日志
+    log("/*/log", DeviceLogMessage.class),
     ;
 
     TopicMessageCodec(String topic, Class type) {

From 67c5e2c8847f125a820ddf9334cb1c0f77caea80 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Thu, 1 Apr 2021 13:41:02 +0800
Subject: [PATCH 10/15] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=A7=E5=93=81ID?=
 =?UTF-8?q?=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../official/JetLinksMqttDeviceMessageCodec.java         | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
index 7441d52..b6594f0 100644
--- a/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/JetLinksMqttDeviceMessageCodec.java
@@ -85,9 +85,12 @@ public class JetLinksMqttDeviceMessageCodec implements DeviceMessageCodec {
                 if (convertResult == null) {
                     return Mono.empty();
                 }
-                return context
-                        .getDevice()
-                        .getConfig(DeviceConfigKey.productId)
+                return Mono
+                        .justOrEmpty(deviceMessage.getHeader("productId").map(String::valueOf))
+                        .switchIfEmpty(context.getDevice(deviceMessage.getDeviceId())
+                                              .flatMap(device -> device
+                                                      .getConfig(DeviceConfigKey.productId))
+                        )
                         .defaultIfEmpty("null")
                         .map(productId -> SimpleMqttMessage
                                 .builder()

From 6aca5b336bed1e2f43007f80e200022a59499115 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Thu, 1 Apr 2021 16:04:55 +0800
Subject: [PATCH 11/15] add source plugin

---
 pom.xml | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/pom.xml b/pom.xml
index bd112df..80f9bb3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -135,6 +135,21 @@
                     8
                 
             
+
+
+            
+                org.apache.maven.plugins
+                maven-source-plugin
+                2.4
+                
+                    
+                        attach-sources
+                        
+                            jar-no-fork
+                        
+                    
+                
+            
         
     
 

From 73dd691413bd91c73efd3ce9c7a82477c8913902 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Thu, 1 Apr 2021 16:06:36 +0800
Subject: [PATCH 12/15] new package

---
 ...etlinks-official-protocol-2.0-SNAPSHOT.jar | Bin 30431 -> 31089 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/package/jetlinks-official-protocol-2.0-SNAPSHOT.jar b/package/jetlinks-official-protocol-2.0-SNAPSHOT.jar
index db1a02752e8fb0b7fc3725dca81abf1194cf0444..a42c6740fa9359f737fd66249d76e08a4324a092 100644
GIT binary patch
delta 10207
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
HKZgGYqIta;

delta 9557
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
UB6_&|CCWjK{IL<$T>sJhKNF^PZ~y=R


From 4d194d2cc24cdd2bd28eaa82b0a7539dd930a33f Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Sun, 25 Apr 2021 17:13:55 +0800
Subject: [PATCH 13/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0child-reply?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml                                       |  2 +-
 .../protocol/official/TopicMessageCodec.java  | 41 +++++++++++++++++++
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 80f9bb3..f2ae6d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -158,7 +158,7 @@
         
             org.jetlinks
             jetlinks-supports
-            1.1.5
+            1.1.6-SNAPSHOT
         
 
         
diff --git a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
index d26d40d..343a4e1 100644
--- a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
@@ -9,6 +9,8 @@ import org.jetlinks.core.message.firmware.*;
 import org.jetlinks.core.message.function.FunctionInvokeMessage;
 import org.jetlinks.core.message.function.FunctionInvokeMessageReply;
 import org.jetlinks.core.message.property.*;
+import org.jetlinks.core.message.state.DeviceStateCheckMessage;
+import org.jetlinks.core.message.state.DeviceStateCheckMessageReply;
 import org.jetlinks.core.utils.TopicUtils;
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Flux;
@@ -70,6 +72,42 @@ public enum TopicMessageCodec {
             payload.setTopic(String.join("/", topic));
             return payload;
 
+        }
+    }, //子设备消息回复
+    childReply("/*/child-reply/*/**", ChildDeviceMessageReply.class) {
+        @Override
+        public Publisher doDecode(ObjectMapper mapper, String[] topic, byte[] payload) {
+            String[] _topic = Arrays.copyOfRange(topic, 2, topic.length);
+            _topic[0] = "";// topic以/开头所有第一位是空白
+            return TopicMessageCodec
+                    .decode(mapper, _topic, payload)
+                    .map(childMsg -> {
+                        ChildDeviceMessageReply msg = new ChildDeviceMessageReply();
+                        msg.setDeviceId(topic[1]);
+                        msg.setChildDeviceMessage(childMsg);
+                        msg.setTimestamp(childMsg.getTimestamp());
+                        msg.setMessageId(childMsg.getMessageId());
+                        return msg;
+                    });
+        }
+
+        @Override
+        protected TopicPayload doEncode(ObjectMapper mapper, String[] topics, DeviceMessage message) {
+            ChildDeviceMessageReply deviceMessage = ((ChildDeviceMessageReply) message);
+
+            DeviceMessage childMessage = ((DeviceMessage) deviceMessage.getChildDeviceMessage());
+
+            TopicPayload payload = TopicMessageCodec.encode(mapper, childMessage);
+            String[] childTopic = payload.getTopic().split("/");
+            String[] topic = new String[topics.length + childTopic.length - 3];
+            //合并topic
+            System.arraycopy(topics, 0, topic, 0, topics.length - 1);
+            System.arraycopy(childTopic, 1, topic, topics.length - 2, childTopic.length - 1);
+
+            refactorTopic(topic, message);
+            payload.setTopic(String.join("/", topic));
+            return payload;
+
         }
     },
     //更新标签
@@ -110,6 +148,9 @@ public enum TopicMessageCodec {
     offline("/*/offline", DeviceOfflineMessage.class),
     //日志
     log("/*/log", DeviceLogMessage.class),
+    //状态检查
+    stateCheck("/*/state-check", DeviceStateCheckMessage.class),
+    stateCheckReply("/*/state-check/reply", DeviceStateCheckMessageReply.class),
     ;
 
     TopicMessageCodec(String topic, Class type) {

From fd8358ad0a43740fd72ddc1f7c06f3f1402970e7 Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Fri, 21 May 2021 15:04:48 +0800
Subject: [PATCH 14/15] =?UTF-8?q?=E6=8B=89=E5=8F=96=E5=9B=BA=E4=BB=B6?=
 =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=9B=9E=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../java/org/jetlinks/protocol/official/TopicMessageCodec.java  | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
index 343a4e1..084541b 100644
--- a/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
+++ b/src/main/java/org/jetlinks/protocol/official/TopicMessageCodec.java
@@ -122,6 +122,8 @@ public enum TopicMessageCodec {
     upgradeProcessFirmware("/*/firmware/upgrade/progress", UpgradeFirmwareProgressMessage.class),
     //拉取固件
     requestFirmware("/*/firmware/pull", RequestFirmwareMessage.class),
+    //拉取固件更新回复
+    requestFirmwareReply("/*/firmware/pull/reply", RequestFirmwareMessageReply.class),
     //上报固件版本
     reportFirmware("/*/firmware/report", ReportFirmwareMessage.class),
     //读取固件回复

From c87c98aee3fabb0dfe1fe117a159e187c6e2d10a Mon Sep 17 00:00:00 2001
From: zhou-hao 
Date: Fri, 21 May 2021 15:05:31 +0800
Subject: [PATCH 15/15] =?UTF-8?q?=E6=8B=89=E5=8F=96=E5=9B=BA=E4=BB=B6?=
 =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=9B=9E=E5=A4=8D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...etlinks-official-protocol-2.0-SNAPSHOT.jar | Bin 31089 -> 33134 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/package/jetlinks-official-protocol-2.0-SNAPSHOT.jar b/package/jetlinks-official-protocol-2.0-SNAPSHOT.jar
index a42c6740fa9359f737fd66249d76e08a4324a092..117faa2dd2b3b7cb10277118ac53c3d29c215f9d 100644
GIT binary patch
delta 11572
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 delta 9654 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 i0{)nW|2Y)?7xD*#=>7-u5{no>3