├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── org │ │ └── jetlinks │ │ └── demo │ │ └── protocol │ │ ├── DemoProtocolSupportProvider.java │ │ ├── TopicMessage.java │ │ ├── TopicMessageCodec.java │ │ ├── coap │ │ └── CoAPDeviceMessageCodec.java │ │ ├── http │ │ ├── HttpClientDeviceMessageCodec.java │ │ ├── HttpClientDeviceSession.java │ │ └── HttpDeviceMessageCodec.java │ │ ├── mqtt │ │ └── MqttDeviceMessageCodec.java │ │ ├── tcp │ │ ├── DemoTcpMessage.java │ │ ├── DemoTcpMessageCodec.java │ │ ├── MessageType.java │ │ ├── TcpDeviceMessage.java │ │ ├── TcpPayload.java │ │ ├── TcpStatus.java │ │ ├── client │ │ │ ├── TcpClientDeviceSession.java │ │ │ └── TcpClientMessageSupport.java │ │ └── message │ │ │ ├── AuthRequest.java │ │ │ ├── AuthResponse.java │ │ │ ├── ErrorMessage.java │ │ │ ├── FireAlarm.java │ │ │ ├── Ping.java │ │ │ ├── Pong.java │ │ │ ├── ReadProperty.java │ │ │ ├── ReportProperty.java │ │ │ ├── TemperatureReport.java │ │ │ └── WriteProperty.java │ │ ├── udp │ │ ├── DemoUdpMessage.java │ │ ├── DemoUdpMessageCodec.java │ │ └── message │ │ │ ├── AuthRequest.java │ │ │ ├── FireAlarm.java │ │ │ ├── ReportProperty.java │ │ │ └── UdpPayload.java │ │ └── websocket │ │ └── WebsocketDeviceMessageCodec.java └── resources │ └── services │ └── org.jetlinks.core.spi.ProtocolSupportProvider └── test └── java └── org └── jetlinks └── demo └── protocol ├── DemoTcpSimulator.java ├── TopicMessageCodecTest.java └── tcp ├── DemoTcpMessageTest.java └── message ├── AuthRequestTest.java ├── AuthResponseTest.java ├── FireAlarmTest.java └── TemperatureReportTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | **/pom.xml.versionsBackup 2 | **/target/ 3 | **/out/ 4 | *.class 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | .idea/ 8 | /nbproject 9 | *.ipr 10 | *.iws 11 | *.iml 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.log 18 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 19 | hs_err_pid* 20 | **/transaction-logs/ 21 | !/.mvn/wrapper/maven-wrapper.jar 22 | /data/ 23 | *.db 24 | /static/ 25 | /upload 26 | /ui/upload/ 27 | docker/data 28 | !ip2region.db 29 | !device-simulator.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 演示自定义消息编解码协议 2 | 3 | 此协议已弃用,请使用官方协议 https://github.com/jetlinks/jetlinks-official-protocol 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.jetlinks.thidparty 8 | demo-protocol 9 | 1.0 10 | 11 | UTF-8 12 | zh_CN 13 | 1.8 14 | ${java.version} 15 | 4.1.51.Final 16 | 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 3.1 24 | 25 | ${project.build.jdk} 26 | ${project.build.jdk} 27 | ${project.build.sourceEncoding} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.jetlinks 36 | jetlinks-core 37 | 1.1.6 38 | 39 | 40 | org.jetlinks 41 | jetlinks-supports 42 | 1.1.6 43 | 44 | 45 | org.eclipse.californium 46 | californium-core 47 | 2.2.1 48 | true 49 | 50 | 51 | org.projectlombok 52 | lombok 53 | 1.18.10 54 | 55 | 56 | io.vertx 57 | vertx-core 58 | 3.8.3 59 | test 60 | 61 | 62 | org.junit.jupiter 63 | junit-jupiter 64 | 5.5.2 65 | test 66 | 67 | 68 | 69 | ch.qos.logback 70 | logback-classic 71 | 1.2.3 72 | 73 | 74 | io.vertx 75 | vertx-core 76 | 3.8.3 77 | compile 78 | 79 | 80 | 81 | org.springframework 82 | spring-webflux 83 | 5.2.5.RELEASE 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | io.netty 92 | netty-bom 93 | ${netty.version} 94 | pom 95 | import 96 | 97 | 98 | 99 | 100 | 101 | 102 | hsweb-nexus 103 | Nexus Release Repository 104 | https://nexus.jetlinks.cn/content/groups/public/ 105 | 106 | true 107 | always 108 | 109 | 110 | 111 | aliyun-nexus 112 | aliyun 113 | https://maven.aliyun.com/nexus/content/groups/public/ 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/DemoProtocolSupportProvider.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol; 2 | 3 | import org.jetlinks.core.ProtocolSupport; 4 | import org.jetlinks.core.Value; 5 | import org.jetlinks.core.defaults.Authenticator; 6 | import org.jetlinks.core.defaults.CompositeProtocolSupport; 7 | import org.jetlinks.core.device.*; 8 | import org.jetlinks.core.device.manager.DeviceBindManager; 9 | import org.jetlinks.core.message.codec.DefaultTransport; 10 | import org.jetlinks.core.metadata.DefaultConfigMetadata; 11 | import org.jetlinks.core.metadata.DeviceConfigScope; 12 | import org.jetlinks.core.metadata.types.IntType; 13 | import org.jetlinks.core.metadata.types.PasswordType; 14 | import org.jetlinks.core.metadata.types.StringType; 15 | import org.jetlinks.core.server.session.DeviceSessionManager; 16 | import org.jetlinks.core.spi.ProtocolSupportProvider; 17 | import org.jetlinks.core.spi.ServiceContext; 18 | import org.jetlinks.demo.protocol.coap.CoAPDeviceMessageCodec; 19 | import org.jetlinks.demo.protocol.http.HttpClientDeviceMessageCodec; 20 | import org.jetlinks.demo.protocol.http.HttpDeviceMessageCodec; 21 | import org.jetlinks.demo.protocol.mqtt.MqttDeviceMessageCodec; 22 | import org.jetlinks.demo.protocol.tcp.DemoTcpMessageCodec; 23 | import org.jetlinks.demo.protocol.udp.DemoUdpMessageCodec; 24 | import org.jetlinks.demo.protocol.websocket.WebsocketDeviceMessageCodec; 25 | import org.jetlinks.supports.official.JetLinksDeviceMetadataCodec; 26 | import reactor.core.publisher.Mono; 27 | 28 | import javax.annotation.Nonnull; 29 | 30 | public class DemoProtocolSupportProvider implements ProtocolSupportProvider { 31 | 32 | 33 | @Override 34 | public void dispose() { 35 | //协议卸载时执行 36 | } 37 | 38 | private static final DefaultConfigMetadata mqttConfig = new DefaultConfigMetadata( 39 | "MQTT认证配置" 40 | , "") 41 | .add("username", "username", "MQTT用户名", StringType.GLOBAL) 42 | .add("password", "password", "MQTT密码", PasswordType.GLOBAL); 43 | 44 | 45 | private static final DefaultConfigMetadata tcpConfig = new DefaultConfigMetadata( 46 | "TCP认证配置" 47 | , "") 48 | .add("tcp_auth_key", "key", "TCP认证KEY", StringType.GLOBAL); 49 | 50 | private static final DefaultConfigMetadata udpConfig = new DefaultConfigMetadata( 51 | "UDP认证配置" 52 | , "") 53 | .add("udp_auth_key", "key", "UDP认证KEY", StringType.GLOBAL); 54 | 55 | private static final DefaultConfigMetadata tcpClientConfig = new DefaultConfigMetadata( 56 | "远程服务配置" 57 | , "") 58 | .add("host", "host", "host", StringType.GLOBAL, DeviceConfigScope.product)//只需要产品配置 59 | .add("port", "port", "host", IntType.GLOBAL, DeviceConfigScope.product);//只需要产品配置 60 | 61 | @Override 62 | public Mono create(ServiceContext context) { 63 | CompositeProtocolSupport support = new CompositeProtocolSupport(); 64 | support.setId("demo-v1"); 65 | support.setName("演示协议v1"); 66 | support.setDescription("演示协议"); 67 | //固定为JetLinksDeviceMetadataCodec 68 | support.setMetadataCodec(new JetLinksDeviceMetadataCodec()); 69 | 70 | //TCP 演示协议 71 | { 72 | DemoTcpMessageCodec codec = new DemoTcpMessageCodec(); 73 | support.addMessageCodecSupport(DefaultTransport.TCP, () -> Mono.just(codec)); 74 | support.addMessageCodecSupport(DefaultTransport.TCP_TLS, () -> Mono.just(codec)); 75 | support.addConfigMetadata(DefaultTransport.TCP, tcpConfig); 76 | support.addConfigMetadata(DefaultTransport.TCP_TLS, tcpConfig); 77 | 78 | } 79 | 80 | { 81 | //UDP消息编解码器 82 | DemoUdpMessageCodec codec = new DemoUdpMessageCodec(); 83 | support.addMessageCodecSupport(DefaultTransport.UDP, () -> Mono.just(codec)); 84 | support.addMessageCodecSupport(DefaultTransport.UDP_DTLS, () -> Mono.just(codec)); 85 | 86 | support.addConfigMetadata(DefaultTransport.UDP, udpConfig); 87 | support.addConfigMetadata(DefaultTransport.UDP_DTLS, udpConfig); 88 | } 89 | 90 | { 91 | //MQTT消息编解码器 92 | MqttDeviceMessageCodec codec = new MqttDeviceMessageCodec(); 93 | support.addMessageCodecSupport(DefaultTransport.MQTT, () -> Mono.just(codec)); 94 | } 95 | 96 | { 97 | //DeviceBindManager 支持绑定第三方平台信息 98 | // context.getService(DeviceBindManager.class) 99 | // .ifPresent(bind->{ 100 | //HTTP 101 | HttpDeviceMessageCodec codec = new HttpDeviceMessageCodec(); 102 | support.addMessageCodecSupport(DefaultTransport.HTTP, () -> Mono.just(codec)); 103 | // }); 104 | } 105 | 106 | // { 107 | // //HTTP Client 108 | // Mono.zip( 109 | // Mono.justOrEmpty(context.getService(DeviceRegistry.class)), 110 | // Mono.justOrEmpty(context.getService(DeviceSessionManager.class)), 111 | // HttpClientDeviceMessageCodec::new 112 | // ) 113 | // .subscribe(codec -> { 114 | // 115 | // //注册到协议 116 | // //support.addMessageCodecSupport(DefaultTransport.HTTP, () -> Mono.just(codec)); 117 | // 118 | // }); 119 | // } 120 | 121 | 122 | { 123 | //CoAP 124 | CoAPDeviceMessageCodec codec = new CoAPDeviceMessageCodec(); 125 | support.addMessageCodecSupport(DefaultTransport.CoAP, () -> Mono.just(codec)); 126 | } 127 | 128 | { 129 | //WebSocket 130 | WebsocketDeviceMessageCodec codec = new WebsocketDeviceMessageCodec(); 131 | support.addMessageCodecSupport(DefaultTransport.WebSocket, () -> Mono.just(codec)); 132 | } 133 | 134 | //MQTT需要的配置信息 135 | support.addConfigMetadata(DefaultTransport.MQTT, mqttConfig); 136 | //MQTT认证策略 137 | support.addAuthenticator(DefaultTransport.MQTT, new Authenticator() { 138 | @Override 139 | //使用clientId作为设备ID时的认证方式 140 | public Mono authenticate(@Nonnull AuthenticationRequest request, @Nonnull DeviceOperator device) { 141 | MqttAuthenticationRequest mqttRequest = ((MqttAuthenticationRequest) request); 142 | return device.getConfigs("username", "password") 143 | .flatMap(values -> { 144 | String username = values.getValue("username").map(Value::asString).orElse(null); 145 | String password = values.getValue("password").map(Value::asString).orElse(null); 146 | if (mqttRequest.getUsername().equals(username) && mqttRequest 147 | .getPassword() 148 | .equals(password)) { 149 | return Mono.just(AuthenticationResponse.success()); 150 | } else { 151 | return Mono.just(AuthenticationResponse.error(400, "密码错误")); 152 | } 153 | 154 | }); 155 | } 156 | 157 | @Override 158 | //在网关中配置使用指定的认证协议时的认证方式 159 | public Mono authenticate(@Nonnull AuthenticationRequest request, @Nonnull DeviceRegistry registry) { 160 | MqttAuthenticationRequest mqttRequest = ((MqttAuthenticationRequest) request); 161 | return registry 162 | .getDevice(mqttRequest.getUsername()) //用户名作为设备ID 163 | .flatMap(device -> device 164 | .getSelfConfig("password").map(Value::asString) //密码 165 | .flatMap(password -> { 166 | if (password.equals(mqttRequest.getPassword())) { 167 | //认证成功,需要返回设备ID 168 | return Mono.just(AuthenticationResponse.success(mqttRequest.getUsername())); 169 | } else { 170 | return Mono.just(AuthenticationResponse.error(400, "密码错误")); 171 | } 172 | })); 173 | } 174 | }); 175 | 176 | // //tcp client,通过tcp客户端连接其他服务处理设备消息 177 | // { 178 | // support.addConfigMetadata(DefaultTransport.TCP, tcpClientConfig); 179 | // return Mono 180 | // .zip( 181 | // Mono.justOrEmpty(context.getService(DecodedClientMessageHandler.class)), 182 | // Mono.justOrEmpty(context.getService(DeviceSessionManager.class)), 183 | // Mono.justOrEmpty(context.getService(Vertx.class)) 184 | // ) 185 | // .map(tp3 -> new TcpClientMessageSupport(tp3.getT1(), tp3.getT2(), tp3.getT3())) 186 | // .doOnNext(tcp -> { 187 | // //设置状态检查 188 | // support.setDeviceStateChecker(tcp); 189 | // support.doOnDispose(tcp); //协议失效时执行 190 | // }) 191 | // .thenReturn(support); 192 | // 193 | // } 194 | 195 | return Mono.just(support); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/TopicMessage.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.jetlinks.core.message.DeviceMessage; 7 | 8 | @Getter 9 | @Setter 10 | @AllArgsConstructor 11 | public class TopicMessage { 12 | 13 | private String topic; 14 | 15 | private Object message; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/TopicMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import io.netty.buffer.Unpooled; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.hswebframework.web.id.IDGenerator; 8 | import org.jetlinks.core.message.*; 9 | import org.jetlinks.core.message.codec.SimpleMqttMessage; 10 | import org.jetlinks.core.message.event.EventMessage; 11 | import org.jetlinks.core.message.firmware.*; 12 | import org.jetlinks.core.message.function.FunctionInvokeMessage; 13 | import org.jetlinks.core.message.function.FunctionInvokeMessageReply; 14 | import org.jetlinks.core.message.property.*; 15 | 16 | import java.util.HashMap; 17 | 18 | 19 | @Slf4j 20 | public class TopicMessageCodec { 21 | 22 | protected DeviceMessage doDecode(String deviceId, String topic, JSONObject payload) { 23 | if (!topic.startsWith("/")) { 24 | topic = "/" + topic; 25 | } 26 | DeviceMessage message = null; 27 | if (topic.startsWith("/fire_alarm")) { 28 | message = handleFireAlarm(topic, payload); 29 | } else if (topic.startsWith("/fault_alarm")) { 30 | message = handleFaultAlarm(topic, payload); 31 | } else if (topic.startsWith("/register")) { 32 | message = handleRegister(payload); 33 | } else if (topic.startsWith("/unregister")) { 34 | message = handleUnRegister(payload); 35 | } else if (topic.startsWith("/dev_msg")) { 36 | message = handleDeviceMessage(topic, payload); 37 | } else if (topic.startsWith("/device_online_status")) { 38 | message = handleDeviceOnlineStatus(topic, payload); 39 | } else if (topic.startsWith("/read-property")) { 40 | message = handleReadPropertyReply(payload); 41 | } else if (topic.startsWith("/report-property")) { //定时上报属性 42 | message = handleReportProperty(payload); 43 | } else if (topic.startsWith("/write-property")) { 44 | message = handleWritePropertyReply(payload); 45 | } else if (topic.startsWith("/invoke-function")) { 46 | message = handleFunctionInvokeReply(payload); 47 | } else if (topic.startsWith("/open-door")) { 48 | message = handleOpenTheDoor(topic, payload); 49 | } else if (topic.startsWith("/children")) { 50 | ChildDeviceMessage childDeviceMessage = new ChildDeviceMessage(); 51 | childDeviceMessage.setDeviceId(deviceId); 52 | DeviceMessage children = doDecode(deviceId, topic.substring(9), payload); 53 | childDeviceMessage.setChildDeviceMessage(children); 54 | childDeviceMessage.setChildDeviceId(children.getDeviceId()); 55 | message = childDeviceMessage; 56 | } 57 | // 固件相关1.0.3版本后增加,注意: 专业版中才有固件相关业务功能 58 | else if (topic.startsWith("/firmware/report")) {//上报固件信息 59 | message = payload.toJavaObject(ReportFirmwareMessage.class); 60 | } else if (topic.startsWith("/firmware/progress")) { //上报升级进度 61 | message = payload.toJavaObject(UpgradeFirmwareProgressMessage.class); 62 | } else if (topic.startsWith("/firmware/pull")) { //拉取固件信息 63 | message = payload.toJavaObject(RequestFirmwareMessage.class); 64 | } else if (topic.startsWith("/tags")) { //更新tags 65 | message = payload.toJavaObject(UpdateTagMessage.class); 66 | } else if (topic.startsWith("/offline")) { 67 | message = payload.toJavaObject(DeviceOfflineMessage.class); 68 | }else if (topic.startsWith("/online")) { 69 | message = payload.toJavaObject(DeviceOnlineMessage.class); 70 | }else { 71 | message = handleEvent(topic, payload); 72 | } 73 | 74 | log.info("handle demo message:{}:{}", topic, payload); 75 | return message; 76 | } 77 | 78 | 79 | protected TopicMessage doEncode(DeviceMessage message) { 80 | if (message instanceof ReadPropertyMessage) { 81 | String topic = "/read-property"; 82 | JSONObject data = new JSONObject(); 83 | data.put("messageId", message.getMessageId()); 84 | data.put("deviceId", message.getDeviceId()); 85 | data.put("properties", ((ReadPropertyMessage) message).getProperties()); 86 | return new TopicMessage(topic, data); 87 | } else if (message instanceof WritePropertyMessage) { 88 | String topic = "/write-property"; 89 | JSONObject data = new JSONObject(); 90 | data.put("messageId", message.getMessageId()); 91 | data.put("deviceId", message.getDeviceId()); 92 | data.put("properties", ((WritePropertyMessage) message).getProperties()); 93 | return new TopicMessage(topic, data); 94 | } else if (message instanceof FunctionInvokeMessage) { 95 | String topic = "/invoke-function"; 96 | FunctionInvokeMessage invokeMessage = ((FunctionInvokeMessage) message); 97 | JSONObject data = new JSONObject(); 98 | data.put("messageId", message.getMessageId()); 99 | data.put("deviceId", message.getDeviceId()); 100 | data.put("function", invokeMessage.getFunctionId()); 101 | data.put("args", invokeMessage.getInputs()); 102 | return new TopicMessage(topic, data); 103 | } else if (message instanceof ChildDeviceMessage) { 104 | TopicMessage msg = doEncode((DeviceMessage) ((ChildDeviceMessage) message).getChildDeviceMessage()); 105 | if (msg == null) { 106 | return null; 107 | } 108 | String topic = "/children" + msg.getTopic(); 109 | return new TopicMessage(topic, msg.getMessage()); 110 | } 111 | 112 | //平台推送固件更新,设备无需回复此消息. 113 | else if ( 114 | message instanceof UpgradeFirmwareMessage || 115 | message instanceof RequestFirmwareMessageReply 116 | ) { 117 | String topic = "/firmware/push"; 118 | return new TopicMessage(topic, JSON.toJSON(message)); 119 | } 120 | return null; 121 | } 122 | 123 | private FunctionInvokeMessageReply handleFunctionInvokeReply(JSONObject json) { 124 | return json.toJavaObject(FunctionInvokeMessageReply.class); 125 | } 126 | 127 | private DeviceRegisterMessage handleRegister(JSONObject json) { 128 | DeviceRegisterMessage reply = new DeviceRegisterMessage(); 129 | reply.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 130 | reply.setDeviceId(json.getString("deviceId")); 131 | reply.setTimestamp(System.currentTimeMillis()); 132 | reply.setHeaders(json.getJSONObject("headers")); 133 | return reply; 134 | } 135 | 136 | private DeviceUnRegisterMessage handleUnRegister(JSONObject json) { 137 | DeviceUnRegisterMessage reply = new DeviceUnRegisterMessage(); 138 | reply.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 139 | reply.setDeviceId(json.getString("deviceId")); 140 | reply.setTimestamp(System.currentTimeMillis()); 141 | return reply; 142 | } 143 | 144 | private ReportPropertyMessage handleReportProperty(JSONObject json) { 145 | ReportPropertyMessage msg = ReportPropertyMessage.create(); 146 | msg.fromJson(json); 147 | return msg; 148 | } 149 | 150 | private ReadPropertyMessageReply handleReadPropertyReply(JSONObject json) { 151 | return json.toJavaObject(ReadPropertyMessageReply.class); 152 | } 153 | 154 | private WritePropertyMessageReply handleWritePropertyReply(JSONObject json) { 155 | return json.toJavaObject(WritePropertyMessageReply.class); 156 | } 157 | 158 | private EventMessage handleFireAlarm(String topic, JSONObject json) { 159 | EventMessage eventMessage = new EventMessage(); 160 | 161 | eventMessage.setDeviceId(json.getString("deviceId")); 162 | eventMessage.setEvent("fire_alarm"); 163 | eventMessage.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 164 | 165 | eventMessage.setData(new HashMap<>(json)); 166 | return eventMessage; 167 | } 168 | 169 | private DeviceMessage handleEvent(String topic, JSONObject json) { 170 | EventMessage eventMessage = new EventMessage(); 171 | 172 | eventMessage.setDeviceId(json.getString("deviceId")); 173 | eventMessage.setEvent(topic.substring(1)); 174 | eventMessage.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 175 | 176 | eventMessage.setData(new HashMap<>(json)); 177 | return eventMessage; 178 | } 179 | 180 | 181 | private EventMessage handleOpenTheDoor(String topic, JSONObject json) { 182 | EventMessage eventMessage = new EventMessage(); 183 | 184 | eventMessage.setDeviceId(json.getString("deviceId")); 185 | eventMessage.setEvent("open-door"); 186 | eventMessage.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 187 | 188 | eventMessage.setData(new HashMap<>(json)); 189 | return eventMessage; 190 | } 191 | 192 | private EventMessage handleFaultAlarm(String topic, JSONObject json) { 193 | // String[] topics = topic.split("[/]"); 194 | EventMessage eventMessage = new EventMessage(); 195 | 196 | eventMessage.setDeviceId(json.getString("deviceId")); 197 | eventMessage.setEvent("fault_alarm"); 198 | eventMessage.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 199 | eventMessage.setData(new HashMap<>(json)); 200 | return eventMessage; 201 | } 202 | 203 | private EventMessage handleDeviceMessage(String topic, JSONObject json) { 204 | EventMessage eventMessage = new EventMessage(); 205 | 206 | eventMessage.setDeviceId(json.getString("deviceId")); 207 | eventMessage.setEvent("dev_msg"); 208 | eventMessage.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 209 | eventMessage.setData(new HashMap<>(json)); 210 | return eventMessage; 211 | } 212 | 213 | private CommonDeviceMessage handleDeviceOnlineStatus(String topic, JSONObject json) { 214 | CommonDeviceMessage deviceMessage; 215 | 216 | if ("1".equals(json.getString("status"))) { 217 | deviceMessage = new DeviceOnlineMessage(); 218 | } else { 219 | deviceMessage = new DeviceOfflineMessage(); 220 | } 221 | deviceMessage.setDeviceId(json.getString("deviceId")); 222 | 223 | return deviceMessage; 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/coap/CoAPDeviceMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.coap; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import org.eclipse.californium.core.coap.CoAP; 6 | import org.jetlinks.core.message.Message; 7 | import org.jetlinks.core.message.codec.*; 8 | import org.jetlinks.demo.protocol.TopicMessageCodec; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.nio.charset.StandardCharsets; 12 | 13 | 14 | public class CoAPDeviceMessageCodec extends TopicMessageCodec implements DeviceMessageCodec { 15 | 16 | public Transport getSupportTransport() { 17 | return DefaultTransport.CoAP; 18 | } 19 | 20 | @Override 21 | public Mono decode(MessageDecodeContext context) { 22 | 23 | return Mono.defer(() -> { 24 | //CoAP消息 25 | CoapExchangeMessage message = (CoapExchangeMessage) context.getMessage(); 26 | 27 | //path 当成topic 28 | String topic = message.getPath(); 29 | if (!topic.startsWith("/")) { 30 | topic = "/".concat(topic); 31 | } 32 | //转为json 33 | JSONObject payload = message.payloadAsJson(); 34 | 35 | //解码消息 36 | return Mono 37 | .justOrEmpty(doDecode(null, topic, payload)) 38 | .doOnSuccess(msg -> { 39 | if (msg == null) { 40 | //响应成功消息 41 | message.getExchange() 42 | .respond("success"); 43 | } else { 44 | //响应4.04 45 | message.getExchange() 46 | .respond(CoAP.ResponseCode.NOT_FOUND); 47 | 48 | } 49 | }); 50 | }); 51 | } 52 | 53 | public Mono encode(MessageEncodeContext context) { 54 | return Mono.empty(); 55 | 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/http/HttpClientDeviceMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.http; 2 | 3 | import lombok.AllArgsConstructor; 4 | import org.jetlinks.core.Value; 5 | import org.jetlinks.core.device.DeviceOperator; 6 | import org.jetlinks.core.device.DeviceRegistry; 7 | import org.jetlinks.core.device.DeviceState; 8 | import org.jetlinks.core.device.DeviceStateChecker; 9 | import org.jetlinks.core.message.DeviceMessage; 10 | import org.jetlinks.core.message.Message; 11 | import org.jetlinks.core.message.codec.*; 12 | import org.jetlinks.core.message.function.FunctionInvokeMessage; 13 | import org.jetlinks.core.server.session.DeviceSession; 14 | import org.jetlinks.core.server.session.DeviceSessionManager; 15 | import org.reactivestreams.Publisher; 16 | import org.springframework.web.reactive.function.client.WebClient; 17 | import reactor.core.publisher.Mono; 18 | 19 | import javax.annotation.Nonnull; 20 | import javax.validation.constraints.NotNull; 21 | 22 | @AllArgsConstructor 23 | public class HttpClientDeviceMessageCodec implements DeviceMessageCodec, DeviceStateChecker { 24 | private final WebClient webClient; 25 | 26 | private final DeviceRegistry deviceRegistry; 27 | 28 | private final DeviceSessionManager sessionManager; 29 | 30 | public HttpClientDeviceMessageCodec(DeviceRegistry deviceRegistry, DeviceSessionManager sessionManager) { 31 | this(WebClient.create(), deviceRegistry, sessionManager); 32 | } 33 | 34 | @Override 35 | public Transport getSupportTransport() { 36 | return DefaultTransport.HTTP; 37 | } 38 | 39 | @Nonnull 40 | @Override 41 | public Publisher decode(@Nonnull MessageDecodeContext messageDecodeContext) { 42 | return convertReply(messageDecodeContext.getMessage().payloadAsString()); 43 | } 44 | 45 | @Nonnull 46 | @Override 47 | public Publisher encode(@Nonnull MessageEncodeContext messageEncodeContext) { 48 | DeviceMessage message = (DeviceMessage) messageEncodeContext.getMessage(); 49 | DeviceOperator device = messageEncodeContext.getDevice(); 50 | 51 | if (message instanceof FunctionInvokeMessage) { 52 | return createRequestBody(messageEncodeContext.getDevice(), message) 53 | .flatMapMany(body -> webClient 54 | .post() 55 | .uri("/{deviceId}/message", message.getDeviceId()) 56 | .bodyValue(body) 57 | .retrieve() 58 | .bodyToMono(String.class) 59 | .flatMap(json -> convertReply(message, device, json))) 60 | .as(messageEncodeContext::reply) //直接回复结果 61 | .then(Mono.empty()); 62 | } 63 | // TODO: 2020/10/12 其他消息处理 64 | 65 | return Mono.empty(); 66 | } 67 | 68 | //构造消息请求 69 | protected Mono createRequestBody(DeviceOperator device, DeviceMessage message) { 70 | 71 | // TODO: 2020/10/12 72 | return Mono.empty(); 73 | } 74 | 75 | //转换消息响应 76 | protected Mono convertReply(DeviceMessage from, DeviceOperator device, String json) { 77 | 78 | // TODO: 2020/10/12 79 | return Mono.empty(); 80 | } 81 | 82 | //转换消息响应 83 | protected Mono convertReply(String json) { 84 | 85 | // TODO: 2020/10/12 86 | 87 | 88 | return Mono.empty(); 89 | } 90 | 91 | @Override 92 | public @NotNull Mono checkState(@NotNull DeviceOperator device) { 93 | 94 | //获取设备状态 95 | return device 96 | .getConfigs("key") 97 | .flatMap(values -> 98 | webClient 99 | .post() 100 | .uri("/{deviceId}/state", device.getDeviceId()) 101 | //设置key 102 | .header("key", values.getValue("key").map(Value::asString).orElse(null)) 103 | .retrieve() 104 | .bodyToMono(String.class) 105 | .map(json -> { 106 | // TODO: 2020/10/12 根据响应获取状态 107 | byte state = DeviceState.online; 108 | //session不存在则创建session 109 | DeviceSession session = sessionManager.getSession(device.getDeviceId()); 110 | if (session == null) { 111 | sessionManager.register(new HttpClientDeviceSession(device)); 112 | } 113 | return state; 114 | })); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/http/HttpClientDeviceSession.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.http; 2 | 3 | import lombok.Setter; 4 | import org.jetlinks.core.device.DeviceOperator; 5 | import org.jetlinks.core.message.codec.DefaultTransport; 6 | import org.jetlinks.core.message.codec.EncodedMessage; 7 | import org.jetlinks.core.message.codec.Transport; 8 | import org.jetlinks.core.server.session.DeviceSession; 9 | import reactor.core.publisher.Mono; 10 | 11 | import javax.annotation.Nullable; 12 | import java.time.Duration; 13 | 14 | public class HttpClientDeviceSession implements DeviceSession { 15 | 16 | private final DeviceOperator deviceOperator; 17 | 18 | private long lastPingTime = System.currentTimeMillis(); 19 | 20 | private final long connectTime = System.currentTimeMillis(); 21 | 22 | @Setter 23 | private boolean closed; 24 | 25 | private long keepaliveTimeout = Duration.ofMinutes(10).toMillis(); 26 | 27 | public HttpClientDeviceSession(DeviceOperator deviceOperator) { 28 | this.deviceOperator = deviceOperator; 29 | } 30 | 31 | @Override 32 | public String getId() { 33 | return getDeviceId(); 34 | } 35 | 36 | @Override 37 | public String getDeviceId() { 38 | return deviceOperator.getDeviceId(); 39 | } 40 | 41 | @Nullable 42 | @Override 43 | public DeviceOperator getOperator() { 44 | return deviceOperator; 45 | } 46 | 47 | @Override 48 | public long lastPingTime() { 49 | return lastPingTime; 50 | } 51 | 52 | @Override 53 | public long connectTime() { 54 | return connectTime; 55 | } 56 | 57 | @Override 58 | public Mono send(EncodedMessage encodedMessage) { 59 | return Mono.empty(); 60 | } 61 | 62 | @Override 63 | public Transport getTransport() { 64 | return DefaultTransport.TCP; 65 | } 66 | 67 | @Override 68 | public void close() { 69 | 70 | } 71 | 72 | @Override 73 | public void ping() { 74 | lastPingTime = System.currentTimeMillis(); 75 | } 76 | 77 | @Override 78 | public void setKeepAliveTimeout(Duration timeout) { 79 | keepaliveTimeout = timeout.toMillis(); 80 | } 81 | 82 | @Override 83 | public boolean isAlive() { 84 | return !closed && System.currentTimeMillis() - lastPingTime < keepaliveTimeout; 85 | } 86 | 87 | @Override 88 | public void onClose(Runnable call) { 89 | 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/http/HttpDeviceMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.http; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import io.netty.buffer.Unpooled; 5 | import lombok.AllArgsConstructor; 6 | import org.jetlinks.core.device.manager.DeviceBindManager; 7 | import org.jetlinks.core.message.Message; 8 | import org.jetlinks.core.message.RepayableDeviceMessage; 9 | import org.jetlinks.core.message.codec.*; 10 | import org.jetlinks.core.message.codec.http.HttpExchangeMessage; 11 | import org.jetlinks.core.message.codec.http.SimpleHttpResponseMessage; 12 | import org.jetlinks.demo.protocol.TopicMessageCodec; 13 | import org.springframework.http.MediaType; 14 | import reactor.core.publisher.Mono; 15 | 16 | @AllArgsConstructor 17 | public class HttpDeviceMessageCodec extends TopicMessageCodec implements DeviceMessageCodec { 18 | 19 | //WebClient webClient; 20 | 21 | // private DeviceBindManager bindManager; 22 | 23 | public Transport getSupportTransport() { 24 | return DefaultTransport.HTTP; 25 | } 26 | 27 | @Override 28 | public Mono decode(MessageDecodeContext context) { 29 | 30 | return Mono.defer(() -> { 31 | HttpExchangeMessage message = (HttpExchangeMessage) context.getMessage(); 32 | 33 | String topic = message.getUrl(); 34 | JSONObject payload = message.payloadAsJson(); 35 | 36 | return Mono.justOrEmpty(doDecode(null, topic, payload)) 37 | .switchIfEmpty(Mono.defer(() -> { 38 | //未转换成功,响应404 39 | return message.response(SimpleHttpResponseMessage 40 | .builder() 41 | .status(404) 42 | .contentType(MediaType.APPLICATION_JSON) 43 | .payload(Unpooled.wrappedBuffer("{\"success\":false}".getBytes())) 44 | .build()).then(Mono.empty()); 45 | })) 46 | .flatMap(msg -> { 47 | //响应成功 48 | return message.response(SimpleHttpResponseMessage 49 | .builder() 50 | .status(200) 51 | .contentType(MediaType.APPLICATION_JSON) 52 | .payload(Unpooled.wrappedBuffer("{\"success\":true}".getBytes())) 53 | .build()) 54 | .thenReturn(msg); 55 | }); 56 | }); 57 | } 58 | 59 | public Mono encode(MessageEncodeContext context) { 60 | 61 | return context 62 | .reply(((RepayableDeviceMessage) context.getMessage()).newReply().success()) 63 | .then(Mono.empty()); 64 | // 调用第三方接口 65 | // return webClient.post() 66 | // .uri("http://local-host.cn/") 67 | // .retrieve() 68 | // .bodyToMono(Map.class) 69 | // .map(map -> { 70 | // //处理返回值 71 | // DeviceMessage message = null; 72 | // 73 | // return message; 74 | // }) 75 | // .as(context::reply) 76 | // .then(Mono.empty()); 77 | 78 | //return Mono.empty(); 79 | 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/mqtt/MqttDeviceMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.mqtt; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import io.netty.buffer.Unpooled; 6 | import org.jetlinks.core.message.DeviceMessage; 7 | import org.jetlinks.core.message.DisconnectDeviceMessage; 8 | import org.jetlinks.core.message.Message; 9 | import org.jetlinks.core.message.codec.*; 10 | import org.jetlinks.demo.protocol.TopicMessageCodec; 11 | import org.jetlinks.demo.protocol.TopicMessage; 12 | import reactor.core.publisher.Mono; 13 | 14 | import java.nio.charset.StandardCharsets; 15 | 16 | 17 | public class MqttDeviceMessageCodec extends TopicMessageCodec implements DeviceMessageCodec { 18 | public Transport getSupportTransport() { 19 | return DefaultTransport.MQTT; 20 | } 21 | 22 | @Override 23 | public Mono decode(MessageDecodeContext context) { 24 | 25 | return Mono.fromSupplier(() -> { 26 | //转为mqtt消息 27 | MqttMessage mqttMessage = (MqttMessage) context.getMessage(); 28 | 29 | String topic = mqttMessage.getTopic(); 30 | //消息体转为json 31 | JSONObject payload = JSON.parseObject(mqttMessage.getPayload().toString(StandardCharsets.UTF_8)); 32 | 33 | String deviceId = context.getDevice() != null ? context.getDevice().getDeviceId() : null; 34 | 35 | return doDecode(deviceId, topic, payload); 36 | }); 37 | } 38 | 39 | public Mono encode(MessageEncodeContext context) { 40 | Message message = context.getMessage(); 41 | return Mono.defer(() -> { 42 | if (message instanceof DeviceMessage) { 43 | if (message instanceof DisconnectDeviceMessage) { 44 | return ((ToDeviceMessageContext) context) 45 | .disconnect() 46 | .then(Mono.empty()); 47 | } 48 | 49 | TopicMessage msg = doEncode((DeviceMessage) message); 50 | if (null == msg) { 51 | return Mono.empty(); 52 | } 53 | 54 | return Mono.just(SimpleMqttMessage.builder() 55 | .topic(msg.getTopic()) 56 | .payload(Unpooled.wrappedBuffer(JSON.toJSONBytes(msg.getMessage()))) 57 | .build()); 58 | } 59 | return Mono.empty(); 60 | 61 | }); 62 | 63 | } 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/DemoTcpMessage.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.Setter; 8 | import org.jetlinks.core.utils.BytesUtils; 9 | 10 | import java.util.Arrays; 11 | 12 | /** 13 | * demo tcp 报文协议格式 14 | *

15 | * 第0字节为消息类型 16 | * 第1-4字节为消息体长度 17 | * 第5-n为消息体 18 | */ 19 | @Getter 20 | @Setter 21 | @AllArgsConstructor(staticName = "of") 22 | public class DemoTcpMessage { 23 | 24 | private MessageType type; 25 | 26 | private TcpPayload data; 27 | 28 | public static DemoTcpMessage of(byte[] payload) { 29 | MessageType type = MessageType.of(payload).orElseThrow(IllegalArgumentException::new); 30 | return DemoTcpMessage.of(type, type.read(payload, 5)); 31 | } 32 | 33 | public ByteBuf toByteBuf(){ 34 | return Unpooled.wrappedBuffer(toBytes()); 35 | } 36 | 37 | public byte[] toBytes() { 38 | byte[] header = new byte[5]; 39 | header[0] = (byte) type.ordinal(); 40 | 41 | byte[] body = type.toBytes(data); 42 | int bodyLength = body.length; 43 | 44 | BytesUtils.intToLe(header, bodyLength, 1); 45 | 46 | if (bodyLength == 0) { 47 | return header; 48 | } 49 | byte[] data = Arrays.copyOf(header, bodyLength + 5); 50 | System.arraycopy(body, 0, data, 5, bodyLength); 51 | 52 | return data; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "TcpMessage{" + 58 | "type=" + type + 59 | ", data=" + data + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/DemoTcpMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import lombok.AllArgsConstructor; 5 | import lombok.SneakyThrows; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.commons.codec.binary.Hex; 8 | import org.jetlinks.core.Value; 9 | import org.jetlinks.core.message.DeviceMessage; 10 | import org.jetlinks.core.message.DeviceOnlineMessage; 11 | import org.jetlinks.core.message.Message; 12 | import org.jetlinks.core.message.codec.*; 13 | import org.jetlinks.core.message.property.ReadPropertyMessage; 14 | import org.jetlinks.core.message.property.ReportPropertyMessage; 15 | import org.jetlinks.core.message.property.WritePropertyMessage; 16 | import org.jetlinks.core.server.session.DeviceSession; 17 | import org.jetlinks.demo.protocol.tcp.message.*; 18 | import org.reactivestreams.Publisher; 19 | import reactor.core.publisher.Mono; 20 | 21 | import java.util.Arrays; 22 | 23 | @AllArgsConstructor 24 | @Slf4j 25 | public class DemoTcpMessageCodec implements DeviceMessageCodec { 26 | 27 | 28 | @Override 29 | public Transport getSupportTransport() { 30 | return DefaultTransport.TCP; 31 | } 32 | 33 | @Override 34 | @SneakyThrows 35 | public Mono decode(MessageDecodeContext context) { 36 | log.debug("收到消息:"); 37 | return Mono.defer(() -> { 38 | FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context); 39 | byte[] payload = context.getMessage().payloadAsBytes(); 40 | if (log.isDebugEnabled()) { 41 | log.debug("handle tcp message:\n{}", Hex.encodeHexString(payload)); 42 | } 43 | DemoTcpMessage message; 44 | try { 45 | message = DemoTcpMessage.of(payload); 46 | if (log.isDebugEnabled()) { 47 | log.debug("decode tcp message:\n{}\n{}", Hex.encodeHexString(payload), message); 48 | } 49 | } catch (Exception e) { 50 | log.warn("decode tcp message error:[{}]", Hex.encodeHexString(payload), e); 51 | 52 | return Mono.error(e); 53 | } 54 | DeviceSession session = ctx.getSession(); 55 | if (session.getOperator() == null) { 56 | //设备没有认证就发送了消息 57 | if (message.getType() != MessageType.AUTH_REQ) { 58 | log.warn("tcp session[{}], unauthorized.", session.getId()); 59 | return session 60 | .send(EncodedMessage.simple(DemoTcpMessage.of(MessageType.ERROR, ErrorMessage.of(TcpStatus.UN_AUTHORIZED)).toByteBuf())) 61 | .then(Mono.fromRunnable(session::close)); 62 | } 63 | AuthRequest request = ((AuthRequest) message.getData()); 64 | String deviceId = buildDeviceId(request.getDeviceId()); 65 | return context 66 | .getDevice(buildDeviceId(request.getDeviceId())) 67 | .flatMap(operator -> operator.getConfig("tcp_auth_key") 68 | .map(Value::asString) 69 | .filter(key -> Arrays.equals(request.getKey(), key.getBytes())) 70 | .flatMap(msg -> { 71 | //认证通过 72 | DeviceOnlineMessage onlineMessage = new DeviceOnlineMessage(); 73 | onlineMessage.setDeviceId(deviceId); 74 | onlineMessage.setTimestamp(System.currentTimeMillis()); 75 | return session 76 | .send(EncodedMessage.simple(DemoTcpMessage.of(MessageType.AUTH_RES, AuthResponse.of(request.getDeviceId(), TcpStatus.SUCCESS)).toByteBuf())) 77 | .thenReturn(onlineMessage); 78 | })) 79 | //为空可能设备不存在或者没有配置tcp_auth_key,响应错误信息. 80 | .switchIfEmpty(Mono.defer(() -> session 81 | .send(EncodedMessage.simple( 82 | DemoTcpMessage.of(MessageType.AUTH_RES, AuthResponse.of(request.getDeviceId(), TcpStatus.ILLEGAL_ARGUMENTS)).toByteBuf())) 83 | .then(Mono.empty()))); 84 | } 85 | //keepalive, ping pong 86 | if (message.getType() == MessageType.PING) { 87 | return session 88 | .send(EncodedMessage.simple(Unpooled.wrappedBuffer(DemoTcpMessage.of(MessageType.PONG, new Pong()).toBytes()))) 89 | .then(Mono.fromRunnable(session::ping)); 90 | } 91 | if (message.getData() instanceof TcpDeviceMessage) { 92 | return Mono.justOrEmpty(((TcpDeviceMessage) message.getData()).toDeviceMessage()); 93 | } 94 | return Mono.empty(); 95 | }); 96 | } 97 | 98 | public String buildDeviceId(long deviceId) { 99 | return String.valueOf(deviceId); 100 | } 101 | 102 | /** 103 | * demo tcp 报文协议格式 104 | *

105 | * 第0字节为消息类型 106 | * 第1-4字节为消息体长度 107 | * 第5-n为消息体 108 | */ 109 | @Override 110 | public Publisher encode(MessageEncodeContext context) { 111 | Message message = context.getMessage(); 112 | EncodedMessage encodedMessage = null; 113 | log.info("推送设备消息,消息ID:{}", message.getMessageId()); 114 | // 获取设备属性 115 | if (message instanceof ReadPropertyMessage) { 116 | ReadPropertyMessage readPropertyMessage = (ReadPropertyMessage) message; 117 | DemoTcpMessage of = DemoTcpMessage.of(MessageType.READ_PROPERTY, ReadProperty.of(readPropertyMessage)); 118 | encodedMessage = EncodedMessage.simple(of.toByteBuf()); 119 | } 120 | //修改设备属性 121 | if (message instanceof WritePropertyMessage) { 122 | WritePropertyMessage writePropertyMessage = (WritePropertyMessage) message; 123 | DemoTcpMessage of = DemoTcpMessage.of(MessageType.WRITE_PROPERTY, WriteProperty.of(writePropertyMessage)); 124 | encodedMessage = EncodedMessage.simple(of.toByteBuf()); 125 | } 126 | // 设备上报属性 127 | if (message instanceof ReportPropertyMessage) { 128 | ReportPropertyMessage reportPropertyMessage = (ReportPropertyMessage) message; 129 | DemoTcpMessage of = DemoTcpMessage.of(MessageType.REPORT_TEMPERATURE, ReportProperty.of(reportPropertyMessage)); 130 | encodedMessage = EncodedMessage.simple(of.toByteBuf()); 131 | } 132 | return encodedMessage != null ? Mono.just(encodedMessage) : Mono.empty(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/MessageType.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import org.jetlinks.demo.protocol.tcp.message.*; 6 | 7 | import java.util.Optional; 8 | import java.util.function.Supplier; 9 | 10 | @AllArgsConstructor 11 | @Getter 12 | public enum MessageType { 13 | AUTH_REQ("认证请求", AuthRequest::new), 14 | AUTH_RES("认证结果", AuthResponse::new), 15 | ERROR("错误", ErrorMessage::new), 16 | PING("Ping", Ping::new), PONG("Pong", Pong::new), 17 | REPORT_TEMPERATURE("上报温度", TemperatureReport::new), 18 | // READ_TEMPERATURE("读取温度"), 19 | // READ_TEMPERATURE_REPLY("读取温度回复"), 20 | FIRE_ALARM("火警", FireAlarm::new), 21 | READ_PROPERTY("读取设备属性", ReadProperty::new), 22 | WRITE_PROPERTY("修改设备属性", WriteProperty::new), 23 | REPORT_PROPERTY("上报设备属性", ReportProperty::new); 24 | private String text; 25 | 26 | private Supplier payloadSupplier; 27 | 28 | public TcpPayload read(byte[] payload, int offset) { 29 | TcpPayload tcpPayload = payloadSupplier.get(); 30 | tcpPayload.fromBytes(payload, offset); 31 | return tcpPayload; 32 | } 33 | 34 | public byte[] toBytes(TcpPayload data) { 35 | if (data == null) { 36 | return new byte[0]; 37 | } 38 | return data.toBytes(); 39 | } 40 | 41 | public static Optional of(byte[] payload) { 42 | byte type = payload[0]; 43 | MessageType[] values = values(); 44 | if (type > values.length) { 45 | return Optional.empty(); 46 | } 47 | return Optional.of(values()[type]); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/TcpDeviceMessage.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import org.jetlinks.core.message.DeviceMessage; 4 | 5 | public interface TcpDeviceMessage { 6 | 7 | DeviceMessage toDeviceMessage(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/TcpPayload.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import org.jetlinks.core.message.DeviceMessage; 4 | 5 | public interface TcpPayload { 6 | 7 | byte[] toBytes(); 8 | 9 | void fromBytes(byte[] bytes,int offset); 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/TcpStatus.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.util.Optional; 7 | 8 | @AllArgsConstructor 9 | @Getter 10 | public enum TcpStatus { 11 | SUCCESS((byte) 0), 12 | ILLEGAL_ARGUMENTS((byte) 40), 13 | UN_AUTHORIZED((byte) 41), 14 | INTERNAL_SERVER_ERROR((byte) 50), 15 | UNKNOWN((byte) -1), 16 | ; 17 | 18 | private byte status; 19 | 20 | public static Optional of(byte value) { 21 | for (TcpStatus tcpStatus : TcpStatus.values()) { 22 | if (tcpStatus.getStatus() == value) { 23 | return Optional.of(tcpStatus); 24 | } 25 | } 26 | return Optional.empty(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/client/TcpClientDeviceSession.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.client; 2 | 3 | import io.vertx.core.buffer.Buffer; 4 | import io.vertx.core.net.NetSocket; 5 | import io.vertx.core.net.SocketAddress; 6 | import lombok.Setter; 7 | import org.jetlinks.core.device.DeviceOperator; 8 | import org.jetlinks.core.message.codec.DefaultTransport; 9 | import org.jetlinks.core.message.codec.EncodedMessage; 10 | import org.jetlinks.core.message.codec.Transport; 11 | import org.jetlinks.core.server.session.DeviceSession; 12 | import reactor.core.publisher.Mono; 13 | 14 | import javax.annotation.Nullable; 15 | import java.net.InetSocketAddress; 16 | import java.time.Duration; 17 | import java.util.Optional; 18 | 19 | public class TcpClientDeviceSession implements DeviceSession { 20 | 21 | private final DeviceOperator deviceOperator; 22 | 23 | private final NetSocket netSocket; 24 | 25 | private long lastPingTime = System.currentTimeMillis(); 26 | 27 | private final long connectTime = System.currentTimeMillis(); 28 | 29 | @Setter 30 | private boolean closed; 31 | 32 | private long keepaliveTimeout = Duration.ofMinutes(10).toMillis(); 33 | 34 | public TcpClientDeviceSession(DeviceOperator deviceOperator, NetSocket netSocket) { 35 | this.deviceOperator = deviceOperator; 36 | this.netSocket = netSocket; 37 | } 38 | 39 | @Override 40 | public String getId() { 41 | return getDeviceId(); 42 | } 43 | 44 | @Override 45 | public String getDeviceId() { 46 | return deviceOperator.getDeviceId(); 47 | } 48 | 49 | @Nullable 50 | @Override 51 | public DeviceOperator getOperator() { 52 | return deviceOperator; 53 | } 54 | 55 | @Override 56 | public long lastPingTime() { 57 | return lastPingTime; 58 | } 59 | 60 | @Override 61 | public long connectTime() { 62 | return connectTime; 63 | } 64 | 65 | @Override 66 | public Mono send(EncodedMessage encodedMessage) { 67 | return Mono.create(sink -> netSocket.write(Buffer.buffer(encodedMessage.getPayload()), async -> { 68 | if (async.succeeded()) { 69 | sink.success(true); 70 | } else { 71 | sink.error(async.cause()); 72 | } 73 | })); 74 | } 75 | 76 | @Override 77 | public Transport getTransport() { 78 | return DefaultTransport.TCP; 79 | } 80 | 81 | @Override 82 | public void close() { 83 | 84 | } 85 | 86 | @Override 87 | public void ping() { 88 | lastPingTime = System.currentTimeMillis(); 89 | } 90 | 91 | @Override 92 | public Optional getClientAddress() { 93 | return Optional 94 | .ofNullable(netSocket.remoteAddress()) 95 | .map(addr -> new InetSocketAddress(addr.host(), addr.port())); 96 | } 97 | 98 | @Override 99 | public void setKeepAliveTimeout(Duration timeout) { 100 | keepaliveTimeout = timeout.toMillis(); 101 | } 102 | 103 | @Override 104 | public boolean isAlive() { 105 | return !closed && System.currentTimeMillis() - lastPingTime < keepaliveTimeout; 106 | } 107 | 108 | @Override 109 | public void onClose(Runnable call) { 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/client/TcpClientMessageSupport.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.client; 2 | 3 | import io.vertx.core.Vertx; 4 | import io.vertx.core.net.NetClient; 5 | import io.vertx.core.net.NetSocket; 6 | import lombok.AllArgsConstructor; 7 | import org.jetlinks.core.Value; 8 | import org.jetlinks.core.device.DeviceOperator; 9 | import org.jetlinks.core.device.DeviceState; 10 | import org.jetlinks.core.device.DeviceStateChecker; 11 | import org.jetlinks.core.server.session.DeviceSession; 12 | import org.jetlinks.core.server.session.DeviceSessionManager; 13 | import org.jetlinks.supports.server.DecodedClientMessageHandler; 14 | import org.springframework.util.StringUtils; 15 | import reactor.core.Disposable; 16 | import reactor.core.publisher.Mono; 17 | 18 | import javax.validation.constraints.NotNull; 19 | 20 | @AllArgsConstructor 21 | public class TcpClientMessageSupport implements Disposable, DeviceStateChecker { 22 | 23 | private final DecodedClientMessageHandler messageHandler; 24 | 25 | private final DeviceSessionManager sessionManager; 26 | 27 | private final Vertx vertx; 28 | 29 | public Mono getOrCreateSession(DeviceOperator deviceOperator) { 30 | DeviceSession session = sessionManager.getSession(deviceOperator.getDeviceId()); 31 | if (session != null) { 32 | return Mono.just(session); 33 | } 34 | 35 | return deviceOperator 36 | .getSelfConfigs("host", "port") 37 | .flatMap(values -> { 38 | String host = values.getValue("host").map(Value::asString).orElse(null); 39 | int port = values.getValue("port").map(Value::asInt).orElse(0); 40 | if (StringUtils.isEmpty(host) || port == 0) { 41 | return Mono.empty(); 42 | } 43 | 44 | // TODO: 2020/9/22 重试连接逻辑实现 45 | return Mono.create(sink -> vertx 46 | .createNetClient() 47 | .connect(port, host, async -> { 48 | if (async.succeeded()) { 49 | NetSocket socket=async.result(); 50 | DeviceSession deviceSession = new TcpClientDeviceSession(deviceOperator, socket); 51 | handleSocket(socket, deviceOperator); 52 | 53 | sessionManager.register(deviceSession); 54 | sink.success(deviceSession); 55 | } else { 56 | sink.error(async.cause()); 57 | } 58 | })); 59 | }); 60 | 61 | } 62 | 63 | public void handleSocket(NetSocket netSocket, DeviceOperator deviceOperator) { 64 | 65 | netSocket 66 | .handler(buffer -> { 67 | // TODO: 2020/9/22 自定义编解码 68 | 69 | //解码后发送给平台 70 | // messageHandler 71 | // .handleMessage(deviceOperator, message) 72 | // .subscribe(); 73 | }); 74 | } 75 | 76 | @Override 77 | public @NotNull Mono checkState(@NotNull DeviceOperator device) { 78 | return getOrCreateSession(device) 79 | .map(session -> session.isAlive() ? DeviceState.online : DeviceState.offline) 80 | .defaultIfEmpty(DeviceState.offline); 81 | } 82 | 83 | @Override 84 | public void dispose() { 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/AuthRequest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.jetlinks.core.utils.BytesUtils; 8 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 9 | 10 | import java.util.Arrays; 11 | 12 | /** 13 | * 前8为设备ID,低位字节在前. 14 | * 后面为Key. 15 | * 16 | * @author zhouhao 17 | */ 18 | @Getter 19 | @Setter 20 | @AllArgsConstructor 21 | @NoArgsConstructor 22 | public class AuthRequest implements TcpPayload { 23 | 24 | private long deviceId; 25 | 26 | private byte[] key; 27 | 28 | public static AuthRequest of(long deviceId, String key) { 29 | return new AuthRequest(deviceId, key.getBytes()); 30 | } 31 | 32 | public static AuthRequest of(byte[] data) { 33 | AuthRequest message = new AuthRequest(); 34 | message.fromBytes(data, 0); 35 | return message; 36 | } 37 | 38 | @Override 39 | public byte[] toBytes() { 40 | byte[] keyBytes = key == null ? new byte[0] : key; 41 | byte[] idBytes = BytesUtils.longToLe(deviceId); 42 | byte[] data = Arrays.copyOf(idBytes, keyBytes.length + idBytes.length); 43 | System.arraycopy(keyBytes, 0, data, idBytes.length, keyBytes.length); 44 | return data; 45 | } 46 | 47 | @Override 48 | public void fromBytes(byte[] bytes, int offset) { 49 | this.deviceId = BytesUtils.leToLong(bytes, offset, 8); 50 | this.key = Arrays.copyOfRange(bytes, offset + 8, bytes.length); 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "{" + 56 | "deviceId=" + deviceId + 57 | ", key=" + (new String(key)) + 58 | '}'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/AuthResponse.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.jetlinks.core.utils.BytesUtils; 8 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 9 | import org.jetlinks.demo.protocol.tcp.TcpStatus; 10 | 11 | @Getter 12 | @Setter 13 | @AllArgsConstructor(staticName = "of") 14 | @NoArgsConstructor 15 | public class AuthResponse implements TcpPayload { 16 | 17 | private long deviceId; 18 | 19 | private TcpStatus status; 20 | 21 | public static AuthResponse of(byte[] bytes, int offset) { 22 | AuthResponse response = new AuthResponse(); 23 | response.fromBytes(bytes, offset); 24 | return response; 25 | } 26 | 27 | @Override 28 | public byte[] toBytes() { 29 | byte[] bytes = new byte[9]; 30 | BytesUtils.numberToLe(bytes, deviceId, 0, 8); 31 | bytes[8] = status.getStatus(); 32 | return bytes; 33 | } 34 | 35 | @Override 36 | public void fromBytes(byte[] bytes, int offset) { 37 | setDeviceId(BytesUtils.leToLong(bytes, offset, 8)); 38 | setStatus(TcpStatus.of(bytes[offset + 8]).orElse(TcpStatus.UNKNOWN)); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "{" + 44 | "deviceId=" + deviceId + 45 | ", status=" + status + 46 | '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/ErrorMessage.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 8 | import org.jetlinks.demo.protocol.tcp.TcpStatus; 9 | 10 | @Getter 11 | @Setter 12 | @AllArgsConstructor(staticName = "of") 13 | @NoArgsConstructor 14 | public class ErrorMessage implements TcpPayload { 15 | 16 | TcpStatus status; 17 | 18 | @Override 19 | public byte[] toBytes() { 20 | return new byte[]{status.getStatus()}; 21 | } 22 | 23 | @Override 24 | public void fromBytes(byte[] bytes, int offset) { 25 | status = TcpStatus.of(bytes[offset]).orElse(TcpStatus.UNKNOWN); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/FireAlarm.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import lombok.*; 4 | import org.hswebframework.web.id.IDGenerator; 5 | import org.jetlinks.core.message.DeviceMessage; 6 | import org.jetlinks.core.message.event.EventMessage; 7 | import org.jetlinks.core.utils.BytesUtils; 8 | import org.jetlinks.demo.protocol.tcp.TcpDeviceMessage; 9 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | @Getter 15 | @Setter 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @EqualsAndHashCode 20 | @ToString 21 | public class FireAlarm implements TcpPayload, TcpDeviceMessage { 22 | 23 | //设备ID 24 | private long deviceId; 25 | 26 | //经度 27 | private float lnt; 28 | 29 | //纬度 30 | private float lat; 31 | 32 | //点位 33 | private int point; 34 | 35 | private String bName; 36 | 37 | @Override 38 | public DeviceMessage toDeviceMessage() { 39 | EventMessage message = new EventMessage(); 40 | message.setEvent("fire_alarm"); 41 | message.setMessageId(IDGenerator.SNOW_FLAKE_STRING.generate()); 42 | Map map = new HashMap<>(); 43 | map.put("lnt", lnt); 44 | map.put("lat", lat); 45 | map.put("point", point); 46 | map.put("b_name", bName); 47 | message.setData(map); 48 | message.setDeviceId(String.valueOf(deviceId)); 49 | return message; 50 | } 51 | 52 | @Override 53 | public byte[] toBytes() { 54 | //设备id+经度+维度+点位 55 | byte[] data = new byte[8 + 4 + 4 + 4]; 56 | BytesUtils.numberToLe(data, deviceId, 0, 8); 57 | BytesUtils.numberToLe(data, Float.floatToIntBits(lnt), 8, 4); 58 | BytesUtils.numberToLe(data, Float.floatToIntBits(lat), 12, 4); 59 | BytesUtils.numberToLe(data, point, 16, 4); 60 | return data; 61 | } 62 | 63 | @Override 64 | public void fromBytes(byte[] bytes, int offset) { 65 | setDeviceId(BytesUtils.leToLong(bytes, offset, 8)); 66 | setLnt(BytesUtils.leToFloat(bytes, offset + 8, 4)); 67 | setLat(BytesUtils.leToFloat(bytes, offset + 12, 4)); 68 | setPoint(BytesUtils.leToInt(bytes, offset + 16, 4)); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/Ping.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 4 | 5 | public class Ping implements TcpPayload { 6 | @Override 7 | public byte[] toBytes() { 8 | return new byte[0]; 9 | } 10 | 11 | @Override 12 | public void fromBytes(byte[] bytes, int offset) { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/Pong.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 4 | 5 | public class Pong implements TcpPayload { 6 | @Override 7 | public byte[] toBytes() { 8 | return new byte[0]; 9 | } 10 | 11 | @Override 12 | public void fromBytes(byte[] bytes, int offset) { 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/ReadProperty.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import org.jetlinks.core.message.property.ReadPropertyMessage; 9 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 10 | 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.Arrays; 13 | 14 | @Getter 15 | @Setter 16 | @AllArgsConstructor(staticName = "of") 17 | @NoArgsConstructor 18 | public class ReadProperty implements TcpPayload { 19 | 20 | private ReadPropertyMessage readPropertyMessage; 21 | 22 | @Override 23 | public byte[] toBytes() { 24 | return readPropertyMessage.toString().getBytes(); 25 | } 26 | 27 | @Override 28 | public void fromBytes(byte[] bytes, int offset) { 29 | ReadPropertyMessage readPropertyMessage = new ReadPropertyMessage(); 30 | byte[] bytes1 = Arrays.copyOfRange(bytes, offset, bytes.length); 31 | String s = new String(bytes1, StandardCharsets.UTF_8); 32 | readPropertyMessage.fromJson(JSON.parseObject(s)); 33 | this.readPropertyMessage = readPropertyMessage; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/ReportProperty.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import org.jetlinks.core.message.DeviceMessage; 9 | import org.jetlinks.core.message.property.ReportPropertyMessage; 10 | import org.jetlinks.demo.protocol.tcp.TcpDeviceMessage; 11 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 12 | 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Arrays; 15 | 16 | @Getter 17 | @Setter 18 | @NoArgsConstructor 19 | @AllArgsConstructor(staticName = "of") 20 | public class ReportProperty implements TcpDeviceMessage, TcpPayload { 21 | private ReportPropertyMessage reportPropertyMessage; 22 | 23 | @Override 24 | public byte[] toBytes() { 25 | return reportPropertyMessage.toString().getBytes(); 26 | } 27 | 28 | @Override 29 | public void fromBytes(byte[] bytes, int offset) { 30 | ReportPropertyMessage reportPropertyMessage = new ReportPropertyMessage(); 31 | byte[] bytes1 = Arrays.copyOfRange(bytes, offset, bytes.length); 32 | String s = new String(bytes1, StandardCharsets.UTF_8); 33 | reportPropertyMessage.fromJson(JSON.parseObject(s)); 34 | this.reportPropertyMessage = reportPropertyMessage; 35 | } 36 | 37 | @Override 38 | public DeviceMessage toDeviceMessage() { 39 | return reportPropertyMessage; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/TemperatureReport.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import org.jetlinks.core.message.DeviceMessage; 8 | import org.jetlinks.core.message.property.ReportPropertyMessage; 9 | import org.jetlinks.core.utils.BytesUtils; 10 | import org.jetlinks.demo.protocol.tcp.TcpDeviceMessage; 11 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 12 | 13 | import java.util.Collections; 14 | 15 | @Getter 16 | @Setter 17 | @AllArgsConstructor(staticName = "of") 18 | @NoArgsConstructor 19 | public class TemperatureReport implements TcpPayload, TcpDeviceMessage { 20 | 21 | private long deviceId; 22 | 23 | private float temperature; 24 | 25 | @Override 26 | public DeviceMessage toDeviceMessage() { 27 | ReportPropertyMessage message = new ReportPropertyMessage(); 28 | message.setProperties(Collections.singletonMap("temperature", temperature)); 29 | message.setDeviceId(String.valueOf(deviceId)); 30 | message.setTimestamp(System.currentTimeMillis()); 31 | return message; 32 | } 33 | 34 | @Override 35 | public byte[] toBytes() { 36 | //前8位为设备ID,后4位为温度值,低位字节在前. 37 | byte[] data = new byte[12]; 38 | BytesUtils.numberToLe(data, deviceId, 0, 8); 39 | BytesUtils.numberToLe(data, Float.floatToIntBits(temperature), 8, 4); 40 | return data; 41 | } 42 | 43 | @Override 44 | public void fromBytes(byte[] bytes, int offset) { 45 | this.deviceId = BytesUtils.leToLong(bytes, offset, 8); 46 | this.temperature = BytesUtils.leToFloat(bytes, offset + 8, 4); 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "TemperatureReport{" + 52 | "deviceId=" + deviceId + 53 | ", temperature=" + temperature + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/tcp/message/WriteProperty.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import org.jetlinks.core.message.property.WritePropertyMessage; 9 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 10 | 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.Arrays; 13 | 14 | @Getter 15 | @Setter 16 | @NoArgsConstructor 17 | @AllArgsConstructor(staticName = "of") 18 | public class WriteProperty implements TcpPayload { 19 | private WritePropertyMessage writePropertyMessage; 20 | 21 | @Override 22 | public byte[] toBytes() { 23 | return writePropertyMessage.toString().getBytes(); 24 | } 25 | 26 | @Override 27 | public void fromBytes(byte[] bytes, int offset) { 28 | WritePropertyMessage writePropertyMessage = new WritePropertyMessage(); 29 | byte[] bytes1 = Arrays.copyOfRange(bytes, offset, bytes.length); 30 | String s = new String(bytes1, StandardCharsets.UTF_8); 31 | writePropertyMessage.fromJson(JSON.parseObject(s)); 32 | this.writePropertyMessage = writePropertyMessage; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/udp/DemoUdpMessage.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.udp; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | import org.jetlinks.core.message.codec.EncodedMessage; 8 | import org.jetlinks.demo.protocol.tcp.MessageType; 9 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 10 | 11 | /** 12 | * @author wangzheng 13 | * @see 14 | * @since 1.0 15 | */ 16 | 17 | public interface DemoUdpMessage extends EncodedMessage { 18 | 19 | String getType(); 20 | 21 | String getDeviceId(); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/udp/DemoUdpMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.udp; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.Unpooled; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.hswebframework.web.id.IDGenerator; 12 | import org.jetlinks.core.Value; 13 | import org.jetlinks.core.device.DeviceRegistry; 14 | import org.jetlinks.core.message.DeviceMessage; 15 | import org.jetlinks.core.message.DeviceOnlineMessage; 16 | import org.jetlinks.core.message.codec.*; 17 | import org.jetlinks.core.message.event.EventMessage; 18 | import org.jetlinks.core.message.property.ReportPropertyMessage; 19 | import org.jetlinks.core.server.session.DeviceSession; 20 | import org.jetlinks.core.utils.BytesUtils; 21 | import org.jetlinks.demo.protocol.tcp.DemoTcpMessage; 22 | import org.jetlinks.demo.protocol.tcp.MessageType; 23 | import org.jetlinks.demo.protocol.tcp.TcpStatus; 24 | import org.jetlinks.demo.protocol.tcp.message.AuthResponse; 25 | import org.jetlinks.demo.protocol.tcp.message.ErrorMessage; 26 | import org.reactivestreams.Publisher; 27 | import reactor.core.publisher.Flux; 28 | import reactor.core.publisher.Mono; 29 | 30 | import java.nio.charset.StandardCharsets; 31 | import java.util.Arrays; 32 | 33 | /** 34 | * @author wangzheng 35 | * @see 36 | * @since 1.0 37 | */ 38 | @AllArgsConstructor 39 | @Slf4j 40 | public class DemoUdpMessageCodec implements DeviceMessageCodec { 41 | 42 | @Override 43 | public Transport getSupportTransport() { 44 | return DefaultTransport.UDP; 45 | } 46 | 47 | @Override 48 | public Flux decode(MessageDecodeContext context) { 49 | return Flux.defer(() -> { 50 | FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context); 51 | DeviceSession session=ctx.getSession(); 52 | EncodedMessage encodedMessage = context.getMessage(); 53 | JSONObject payload = JSON.parseObject(encodedMessage.getPayload().toString(StandardCharsets.UTF_8)); 54 | return Mono 55 | .justOrEmpty(org.jetlinks.core.message.MessageType.convertMessage(payload)) 56 | // .flatMapMany(msg->{ 57 | // return context 58 | // .getDevice(msg.getDeviceId()) 59 | // .flatMapMany(operator -> operator.getConfig("udp_auth_key") 60 | // .map(Value::asString) 61 | // .filter(key -> key.equals(payload.getString("key"))) 62 | // .flatMapMany(ignore -> { 63 | // //认证通过 64 | // return session 65 | // .send(EncodedMessage.simple(Unpooled.wrappedBuffer("ok".getBytes()))) 66 | // .thenMany(Flux.just(msg)); 67 | // })); 68 | // 69 | // }) .switchIfEmpty(Mono.defer(() -> session 70 | // .send(EncodedMessage.simple(Unpooled.wrappedBuffer("ILLEGAL_ARGUMENTS".getBytes()))) 71 | // .then(Mono.empty()))) 72 | ; 73 | 74 | 75 | }); 76 | } 77 | 78 | @Override 79 | public Publisher encode(MessageEncodeContext context) { 80 | 81 | return Mono.just(EncodedMessage.simple(Unpooled.wrappedBuffer(context.getMessage().toString().getBytes()))); 82 | } 83 | 84 | @Setter 85 | @Getter 86 | @AllArgsConstructor(staticName = "of") 87 | private static class Response { 88 | private MessageType type; 89 | 90 | private Object res; 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/udp/message/AuthRequest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.udp.message; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | import lombok.Setter; 8 | import org.jetlinks.core.message.DeviceMessage; 9 | import org.jetlinks.core.utils.BytesUtils; 10 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 11 | 12 | import javax.annotation.Nonnull; 13 | import java.util.Arrays; 14 | 15 | /** 16 | * 17 | * @author wangzheng 18 | */ 19 | @Getter 20 | @Setter 21 | @AllArgsConstructor 22 | @NoArgsConstructor 23 | public class AuthRequest implements UdpPayload { 24 | 25 | private long deviceId; 26 | 27 | private String key; 28 | 29 | public static AuthRequest of(long deviceId, String key) { 30 | return new AuthRequest(deviceId, key); 31 | } 32 | 33 | 34 | @Override 35 | public String toString() { 36 | return "{" + 37 | "deviceId=" + deviceId + 38 | ", key=" + (new String(key)) + 39 | '}'; 40 | } 41 | 42 | @Override 43 | public String getType() { 44 | return "auth-request"; 45 | } 46 | 47 | @Nonnull 48 | @Override 49 | public ByteBuf getPayload() { 50 | return null; 51 | } 52 | 53 | public String getDeviceId() { 54 | return String.valueOf(this.deviceId); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/udp/message/FireAlarm.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.udp.message; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import lombok.*; 5 | import org.hswebframework.web.id.IDGenerator; 6 | import org.jetlinks.core.message.DeviceMessage; 7 | import org.jetlinks.core.message.event.EventMessage; 8 | import org.jetlinks.core.utils.BytesUtils; 9 | import org.jetlinks.demo.protocol.tcp.TcpDeviceMessage; 10 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 11 | 12 | import javax.annotation.Nonnull; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @Getter 17 | @Setter 18 | @Builder 19 | @NoArgsConstructor 20 | @AllArgsConstructor 21 | @EqualsAndHashCode 22 | @ToString 23 | public class FireAlarm implements UdpPayload { 24 | 25 | //设备ID 26 | private long deviceId; 27 | 28 | //经度 29 | private float lnt; 30 | 31 | //纬度 32 | private float lat; 33 | 34 | //点位 35 | private int point; 36 | 37 | private String bName; 38 | 39 | @Override 40 | public String getType() { 41 | return "fire-alarm"; 42 | } 43 | 44 | 45 | @Nonnull 46 | @Override 47 | public ByteBuf getPayload() { 48 | return null; 49 | } 50 | 51 | public String getDeviceId() { 52 | return String.valueOf(this.deviceId); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/udp/message/ReportProperty.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.udp.message; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import io.netty.buffer.ByteBuf; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Getter; 7 | import lombok.NoArgsConstructor; 8 | import lombok.Setter; 9 | import org.jetlinks.core.message.property.ReportPropertyMessage; 10 | import org.jetlinks.demo.protocol.tcp.TcpPayload; 11 | 12 | import javax.annotation.Nonnull; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Arrays; 15 | 16 | @Getter 17 | @Setter 18 | @NoArgsConstructor 19 | @AllArgsConstructor(staticName = "of") 20 | public class ReportProperty implements UdpPayload { 21 | private ReportPropertyMessage reportPropertyMessage; 22 | 23 | @Override 24 | public String getDeviceId() { 25 | return reportPropertyMessage.getDeviceId(); 26 | } 27 | 28 | @Override 29 | public String getType() { 30 | return "report-property"; 31 | } 32 | 33 | @Nonnull 34 | @Override 35 | public ByteBuf getPayload() { 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/udp/message/UdpPayload.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.udp.message; 2 | 3 | import org.jetlinks.core.message.DeviceMessage; 4 | import org.jetlinks.demo.protocol.udp.DemoUdpMessage; 5 | 6 | /** 7 | * @author wangzheng 8 | * @see 9 | * @since 1.0 10 | */ 11 | public interface UdpPayload extends DemoUdpMessage { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/jetlinks/demo/protocol/websocket/WebsocketDeviceMessageCodec.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.websocket; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import io.netty.buffer.Unpooled; 6 | import org.jetlinks.core.message.DeviceMessage; 7 | import org.jetlinks.core.message.DisconnectDeviceMessage; 8 | import org.jetlinks.core.message.Message; 9 | import org.jetlinks.core.message.codec.*; 10 | import org.jetlinks.core.message.codec.http.websocket.DefaultWebSocketMessage; 11 | import org.jetlinks.core.message.codec.http.websocket.WebSocketMessage; 12 | import org.jetlinks.core.message.codec.http.websocket.WebSocketSession; 13 | import org.jetlinks.core.message.codec.http.websocket.WebSocketSessionMessage; 14 | import org.jetlinks.demo.protocol.TopicMessage; 15 | import org.jetlinks.demo.protocol.TopicMessageCodec; 16 | import reactor.core.publisher.Mono; 17 | 18 | import java.nio.charset.StandardCharsets; 19 | 20 | 21 | public class WebsocketDeviceMessageCodec extends TopicMessageCodec implements DeviceMessageCodec { 22 | 23 | public Transport getSupportTransport() { 24 | return DefaultTransport.WebSocket; 25 | } 26 | 27 | @Override 28 | public Mono decode(MessageDecodeContext context) { 29 | 30 | return Mono.defer(() -> { 31 | WebSocketSessionMessage mqttMessage = (WebSocketSessionMessage) context.getMessage(); 32 | WebSocketSession session = mqttMessage.getWebSocketSession(); 33 | 34 | JSONObject payload = JSON.parseObject(mqttMessage.getPayload().toString(StandardCharsets.UTF_8)); 35 | 36 | return Mono.justOrEmpty(doDecode(null, session.getUri(), payload)) 37 | .switchIfEmpty(Mono.defer(() -> { 38 | //未转换成功,响应404 39 | return session 40 | .send(session.textMessage("{\"status\":404}")) 41 | .then(Mono.empty()); 42 | })); 43 | }); 44 | } 45 | 46 | public Mono encode(MessageEncodeContext context) { 47 | Message message = context.getMessage(); 48 | return Mono.defer(() -> { 49 | if (message instanceof DeviceMessage) { 50 | if (message instanceof DisconnectDeviceMessage) { 51 | return ((ToDeviceMessageContext) context) 52 | .disconnect() 53 | .then(Mono.empty()); 54 | } 55 | 56 | TopicMessage msg = doEncode((DeviceMessage) message); 57 | if (null == msg) { 58 | return Mono.empty(); 59 | } 60 | JSONObject data = new JSONObject(); 61 | data.put("topic", msg.getTopic()); 62 | data.put("message", msg.getMessage()); 63 | 64 | return Mono.just(DefaultWebSocketMessage.of(WebSocketMessage.Type.TEXT, Unpooled.wrappedBuffer(data.toJSONString().getBytes()))); 65 | } 66 | return Mono.empty(); 67 | 68 | }); 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/resources/services/org.jetlinks.core.spi.ProtocolSupportProvider: -------------------------------------------------------------------------------- 1 | org.jetlinks.demo.protocol.DemoProtocolSupportProvider -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/DemoTcpSimulator.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol; 2 | 3 | import io.vertx.core.Handler; 4 | import io.vertx.core.Vertx; 5 | import io.vertx.core.buffer.Buffer; 6 | import io.vertx.core.net.NetSocket; 7 | import io.vertx.core.parsetools.RecordParser; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.commons.codec.binary.Hex; 10 | import org.jetlinks.demo.protocol.tcp.DemoTcpMessage; 11 | import org.jetlinks.demo.protocol.tcp.MessageType; 12 | import org.jetlinks.demo.protocol.tcp.TcpStatus; 13 | import org.jetlinks.demo.protocol.tcp.message.AuthRequest; 14 | import org.jetlinks.demo.protocol.tcp.message.AuthResponse; 15 | import org.jetlinks.demo.protocol.tcp.message.FireAlarm; 16 | import org.jetlinks.demo.protocol.tcp.message.TemperatureReport; 17 | import reactor.core.publisher.Flux; 18 | import reactor.core.publisher.Mono; 19 | 20 | import java.time.Duration; 21 | import java.util.concurrent.ThreadLocalRandom; 22 | import java.util.function.Function; 23 | 24 | @Slf4j 25 | public class DemoTcpSimulator { 26 | 27 | /* 28 | //平台-网络组件-TCP服务-处理方式-脚本-javascript: 29 | //平台侧处理粘拆包的脚本 30 | 31 | var BytesUtils = org.jetlinks.core.utils.BytesUtils; 32 | parser.fixed(5) //1. 固定5字节为报文头,0字节为类型,1-4字节为消息长度(低字节位在前). 33 | .handler(function(buffer){ 34 | var len = BytesUtils.leToInt(buffer.getBytes(),1,4);//2. 获取消息长度. 35 | parser 36 | .fixed(len)//3. 设置下一个包要读取固定长度的数据. 37 | .result(buffer); //4. 设置当前解析的结果 38 | }) 39 | .handler(function(buffer){ 40 | parser.result(buffer) //5. 收到了新的包,则为消息体,设置到结果中,完成后将与步骤4的数据合并为完整的数据包. 41 | .complete(); //6. 完成解析(消息将进入协议中进行解析(DemoTcpMessageCodec)),重置解析器,下一个数据包将从步骤1开始解析. 42 | }); 43 | 44 | */ 45 | public static void main(String[] args) { 46 | Vertx vertx = Vertx.vertx(); 47 | long deviceId = 1001; 48 | String key = "admin"; 49 | vertx.createNetClient() 50 | .connect(5111, "127.0.0.1", result -> { 51 | if (result.succeeded()) { 52 | byte[] login=DemoTcpMessage.of(MessageType.AUTH_REQ, AuthRequest.of(1001, key)).toBytes(); 53 | 54 | NetSocket socket = result.result(); 55 | socket.handler(buffer -> { 56 | // TODO: 2020/3/2 粘拆包处理 57 | DemoTcpMessage tcpMessage = DemoTcpMessage.of(buffer.getBytes()); 58 | System.out.println(tcpMessage); 59 | //认证通过后定时上报温度数据 60 | if (tcpMessage.getType() == MessageType.AUTH_RES && ((AuthResponse) tcpMessage.getData()).getStatus() == TcpStatus.SUCCESS) { 61 | Flux.interval(Duration.ofSeconds(1)) 62 | .flatMap(t -> Flux.just( 63 | DemoTcpMessage.of(MessageType.REPORT_TEMPERATURE, 64 | TemperatureReport.of(deviceId, (float) ThreadLocalRandom.current().nextDouble(20D, 50D))) 65 | .toBytes() 66 | , 67 | DemoTcpMessage.of(MessageType.FIRE_ALARM, 68 | FireAlarm.builder() 69 | .point(ThreadLocalRandom.current().nextInt()) 70 | .lat(102.234F) 71 | .lnt(122.122F) 72 | .deviceId(deviceId) 73 | .build()).toBytes() 74 | )) 75 | .map(Buffer::buffer) 76 | .window(2)//一次性发送2条数据 77 | .flatMap(list -> list.reduce(Buffer::appendBuffer)) 78 | .doOnNext(buf -> socket.write(buf, res -> { 79 | log.debug("send : {}", Hex.encodeHexString(buf.getBytes())); 80 | if (!res.succeeded()) { 81 | res.cause().printStackTrace(); 82 | } 83 | })) 84 | .subscribe(); 85 | } 86 | }).write(Buffer.buffer(login), res -> { 87 | log.debug("send auth req:{}",Hex.encodeHexString(login)); 88 | if (!res.succeeded()) { 89 | res.cause().printStackTrace(); 90 | } 91 | }).exceptionHandler(Throwable::printStackTrace); 92 | 93 | } else { 94 | result.cause().printStackTrace(); 95 | System.exit(0); 96 | } 97 | }); 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/TopicMessageCodecTest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.jetlinks.core.message.ChildDeviceMessage; 5 | import org.jetlinks.core.message.DeviceMessage; 6 | import org.jetlinks.core.message.event.EventMessage; 7 | import org.jetlinks.demo.protocol.mqtt.MqttDeviceMessageCodec; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | class TopicMessageCodecTest { 13 | 14 | @Test 15 | void testChildrenMessage() { 16 | TopicMessageCodec codec = new MqttDeviceMessageCodec(); 17 | 18 | DeviceMessage message = codec.doDecode("test", "/children/fire_alarm", new JSONObject()); 19 | 20 | assertTrue(message instanceof ChildDeviceMessage); 21 | ChildDeviceMessage msg = ((ChildDeviceMessage) message); 22 | assertTrue(msg.getChildDeviceMessage() instanceof EventMessage); 23 | 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/tcp/DemoTcpMessageTest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.ByteBufUtil; 5 | import org.apache.commons.codec.binary.Hex; 6 | import org.apache.commons.lang.RandomStringUtils; 7 | import org.jetlinks.core.message.codec.EncodedMessage; 8 | import org.jetlinks.core.message.property.ReadPropertyMessage; 9 | import org.jetlinks.core.message.property.ReportPropertyMessage; 10 | import org.jetlinks.demo.protocol.tcp.message.*; 11 | import org.junit.jupiter.api.Assertions; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import java.time.LocalDateTime; 15 | import java.util.Collections; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | import java.util.concurrent.ThreadLocalRandom; 19 | 20 | class DemoTcpMessageTest { 21 | 22 | @Test 23 | void test() { 24 | DemoTcpMessage message = DemoTcpMessage.of(MessageType.AUTH_REQ, AuthRequest.of(1000, "admin")); 25 | 26 | byte[] data = message.toBytes(); 27 | System.out.println(Hex.encodeHexString(data)); 28 | 29 | DemoTcpMessage decode = DemoTcpMessage.of(data); 30 | 31 | System.out.println(decode); 32 | 33 | Assertions.assertEquals(message.getType(), decode.getType()); 34 | Assertions.assertArrayEquals(message.getData().toBytes(), decode.getData().toBytes()); 35 | 36 | 37 | } 38 | 39 | @Test 40 | void encodeTest() { 41 | ReadPropertyMessage readPropertyMessage = new ReadPropertyMessage(); 42 | readPropertyMessage.setCode("10001"); 43 | readPropertyMessage.setDeviceId("1000"); 44 | readPropertyMessage.setMessageId("test"); 45 | readPropertyMessage.setTimestamp(LocalDateTime.now().getNano()); 46 | DemoTcpMessage of = DemoTcpMessage.of(MessageType.READ_PROPERTY, ReadProperty.of(readPropertyMessage)); 47 | EncodedMessage simple = EncodedMessage.simple(of.toByteBuf()); 48 | ByteBuf byteBuf = simple.getPayload(); 49 | byte[] payload = ByteBufUtil.getBytes(byteBuf, 0, byteBuf.readableBytes(), false); 50 | DemoTcpMessage message = DemoTcpMessage.of(payload); 51 | System.out.println(message.getType().getText()); 52 | System.out.println(Hex.encodeHexString(payload)); 53 | ReadProperty data = (ReadProperty) message.getData(); 54 | System.out.println(data.getReadPropertyMessage().toString()); 55 | } 56 | 57 | @Test 58 | void encodeCustomTest() { 59 | ReportPropertyMessage readPropertyMessage = new ReportPropertyMessage(); 60 | Map map = new HashMap<>(); 61 | map.put("name", RandomStringUtils.randomAlphanumeric(10)); 62 | map.put("A1", (int) (Math.random() * 100)); 63 | map.put("temperature", Math.random() * 100); 64 | readPropertyMessage.setDeviceId("1000"); 65 | readPropertyMessage.setProperties(map); 66 | DemoTcpMessage of = DemoTcpMessage.of(MessageType.REPORT_PROPERTY, ReportProperty.of(readPropertyMessage)); 67 | EncodedMessage simple = EncodedMessage.simple(of.toByteBuf()); 68 | ByteBuf byteBuf = simple.getPayload(); 69 | byte[] payload = ByteBufUtil.getBytes(byteBuf, 0, byteBuf.readableBytes(), false); 70 | DemoTcpMessage message = DemoTcpMessage.of(payload); 71 | System.out.println(message.getType().getText()); 72 | System.out.println(Hex.encodeHexString(payload)); 73 | ReportProperty data = (ReportProperty) message.getData(); 74 | System.out.println(data.getReportPropertyMessage().toString()); 75 | } 76 | 77 | @Test 78 | void encodeReport() { 79 | float temp = (float) (Math.random() * 1000); 80 | ReportPropertyMessage report = new ReportPropertyMessage(); 81 | report.setProperties(Collections.singletonMap("temperature", temp)); 82 | report.setDeviceId("1000"); 83 | report.setMessageId("test"); 84 | report.setTimestamp(LocalDateTime.now().getNano()); 85 | DemoTcpMessage of = DemoTcpMessage.of(MessageType.REPORT_TEMPERATURE, TemperatureReport.of(1000l, temp)); 86 | EncodedMessage simple = EncodedMessage.simple(of.toByteBuf()); 87 | ByteBuf byteBuf = simple.getPayload(); 88 | byte[] payload = ByteBufUtil.getBytes(byteBuf, 0, byteBuf.readableBytes(), false); 89 | DemoTcpMessage demoTcpMessage = DemoTcpMessage.of(payload); 90 | System.out.println(demoTcpMessage.getType().getText()); 91 | System.out.println(Hex.encodeHexString(payload)); 92 | TcpDeviceMessage data = (TcpDeviceMessage) demoTcpMessage.getData(); 93 | System.out.println(data.toDeviceMessage()); 94 | } 95 | 96 | @Test 97 | void encodeEvent() { 98 | DemoTcpMessage demoTcpMessage = DemoTcpMessage.of(MessageType.FIRE_ALARM, 99 | FireAlarm.builder() 100 | .point(ThreadLocalRandom.current().nextInt()) 101 | .lat(36.5F) 102 | .lnt(122.3F) 103 | .deviceId(1000) 104 | .bName("a") 105 | .build()); 106 | byte[] data = demoTcpMessage.toBytes(); 107 | System.out.println(demoTcpMessage); 108 | System.out.println(Hex.encodeHexString(data)); 109 | //061400000000000000000003e842f43e7742cc77cf1bd9071c 110 | //\06\14\00\00\00\00\00\00\00\00\00\03\e8B\f4>wB\ccw\cf\nt\c6\1a \06\14\00\00\00\00\00\00\00\00\00\03\e8B\f4>wB\ccw\cf\1b\d9\07\1c 111 | //\06\14\00\00\00\00\00\00\00\00\00\03\e8B\f4>wB\ccw\cf\ba\f9#\aa 112 | } 113 | 114 | 115 | } -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/tcp/message/AuthRequestTest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.hswebframework.web.id.IDGenerator; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class AuthRequestTest { 10 | 11 | 12 | @Test 13 | void test() { 14 | AuthRequest message = AuthRequest.of(1000L, "admin"); 15 | 16 | byte[] payload = message.toBytes(); 17 | System.out.println(Hex.encodeHexString(payload)); 18 | 19 | AuthRequest decode = AuthRequest.of(payload); 20 | 21 | System.out.println(decode); 22 | assertEquals(message.getDeviceId(), decode.getDeviceId()); 23 | assertArrayEquals(message.getKey(), decode.getKey()); 24 | 25 | 26 | } 27 | } -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/tcp/message/AuthResponseTest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.hswebframework.web.id.IDGenerator; 5 | import org.jetlinks.demo.protocol.tcp.TcpStatus; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | class AuthResponseTest { 12 | 13 | @Test 14 | void test() { 15 | AuthResponse response = AuthResponse.of(IDGenerator.SNOW_FLAKE.generate(), TcpStatus.SUCCESS); 16 | 17 | byte[] encode = response.toBytes(); 18 | System.out.println(Hex.encodeHexString(encode)); 19 | System.out.println(response); 20 | 21 | AuthResponse decode = AuthResponse.of(encode, 0); 22 | 23 | assertEquals(response.getDeviceId(), decode.getDeviceId()); 24 | assertEquals(response.getStatus(), decode.getStatus()); 25 | 26 | 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/tcp/message/FireAlarmTest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.hswebframework.web.id.IDGenerator; 5 | import org.jetlinks.demo.protocol.tcp.DemoTcpMessage; 6 | import org.jetlinks.demo.protocol.tcp.MessageType; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.concurrent.ThreadLocalRandom; 10 | 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | class FireAlarmTest { 14 | 15 | 16 | @Test 17 | void test() { 18 | 19 | FireAlarm alarm = FireAlarm.builder() 20 | .deviceId(IDGenerator.SNOW_FLAKE.generate()) 21 | .lat(ThreadLocalRandom.current().nextFloat()) 22 | .lnt(ThreadLocalRandom.current().nextFloat()) 23 | .point(ThreadLocalRandom.current().nextInt()) 24 | .build(); 25 | 26 | byte[] bytes = alarm.toBytes(); 27 | System.out.println(Hex.encodeHexString(bytes)); 28 | 29 | FireAlarm decode=new FireAlarm(); 30 | decode.fromBytes(bytes,0); 31 | System.out.println(decode); 32 | assertEquals(alarm,decode); 33 | 34 | } 35 | @Test 36 | void encodeEvent() { 37 | DemoTcpMessage demoTcpMessage = DemoTcpMessage.of(MessageType.FIRE_ALARM, 38 | FireAlarm.builder() 39 | .point(ThreadLocalRandom.current().nextInt()) 40 | .lat(36.5F) 41 | .lnt(122.3F) 42 | .deviceId(1000) 43 | .build()); 44 | byte[] data = demoTcpMessage.toBytes(); 45 | System.out.println(demoTcpMessage); 46 | System.out.println(Hex.encodeHexString(data)); 47 | System.out.println("0614000000e8030000000000009a99f4420000124222b7c94c"); 48 | } 49 | } -------------------------------------------------------------------------------- /src/test/java/org/jetlinks/demo/protocol/tcp/message/TemperatureReportTest.java: -------------------------------------------------------------------------------- 1 | package org.jetlinks.demo.protocol.tcp.message; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.hswebframework.web.id.IDGenerator; 5 | import org.jetlinks.demo.protocol.tcp.DemoTcpMessage; 6 | import org.jetlinks.demo.protocol.tcp.MessageType; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.util.IdGenerator; 10 | 11 | import java.util.concurrent.ThreadLocalRandom; 12 | 13 | import static org.junit.jupiter.api.Assertions.*; 14 | 15 | class TemperatureReportTest { 16 | 17 | 18 | @Test 19 | void test() { 20 | TemperatureReport report = TemperatureReport.of(1000L, 36.82F); 21 | 22 | byte[] encode = report.toBytes(); 23 | System.out.println(Hex.encodeHexString(encode)); 24 | 25 | TemperatureReport decode = new TemperatureReport(); 26 | decode.fromBytes(encode, 0); 27 | System.out.println(decode); 28 | 29 | assertEquals(decode.getDeviceId(),report.getDeviceId()); 30 | assertEquals(decode.getTemperature(),report.getTemperature()); 31 | } 32 | 33 | @Test 34 | void encodeReportProperty() { 35 | DemoTcpMessage demoTcpMessage = DemoTcpMessage.of(MessageType.REPORT_TEMPERATURE, 36 | TemperatureReport.of(1000, 36.82F)); 37 | byte[] data = demoTcpMessage.toBytes(); 38 | System.out.println(demoTcpMessage); 39 | System.out.println(Hex.encodeHexString(data)); 40 | DemoTcpMessage decode = DemoTcpMessage.of(data); 41 | 42 | System.out.println(decode); 43 | Assertions.assertEquals(demoTcpMessage.getType(),decode.getType()); 44 | Assertions.assertArrayEquals(demoTcpMessage.getData().toBytes(),decode.getData().toBytes()); 45 | } 46 | 47 | } --------------------------------------------------------------------------------