Showing
8 changed files
with
963 additions
and
162 deletions
@@ -42,6 +42,7 @@ public class TcpTransportContext extends TransportContext { | @@ -42,6 +42,7 @@ public class TcpTransportContext extends TransportContext { | ||
42 | @Autowired(required = false) | 42 | @Autowired(required = false) |
43 | private TcpSslHandlerProvider sslHandlerProvider; | 43 | private TcpSslHandlerProvider sslHandlerProvider; |
44 | 44 | ||
45 | + /**注入多种数据协议处理器,例如:modbus等*/ | ||
45 | @Getter | 46 | @Getter |
46 | @Autowired | 47 | @Autowired |
47 | private JsonTcpAdaptor jsonTcpAdaptor; | 48 | private JsonTcpAdaptor jsonTcpAdaptor; |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.tcp; | 16 | package org.thingsboard.server.transport.tcp; |
17 | 17 | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
18 | import io.netty.buffer.ByteBuf; | 19 | import io.netty.buffer.ByteBuf; |
19 | import io.netty.buffer.Unpooled; | 20 | import io.netty.buffer.Unpooled; |
20 | import io.netty.channel.ChannelFuture; | 21 | import io.netty.channel.ChannelFuture; |
@@ -27,8 +28,10 @@ import io.netty.util.concurrent.Future; | @@ -27,8 +28,10 @@ import io.netty.util.concurrent.Future; | ||
27 | import io.netty.util.concurrent.GenericFutureListener; | 28 | import io.netty.util.concurrent.GenericFutureListener; |
28 | import lombok.extern.slf4j.Slf4j; | 29 | import lombok.extern.slf4j.Slf4j; |
29 | import org.apache.commons.lang3.StringUtils; | 30 | import org.apache.commons.lang3.StringUtils; |
30 | -import org.thingsboard.server.common.data.*; | ||
31 | -import org.thingsboard.server.common.data.device.profile.MqttTopics; | 31 | +import org.thingsboard.server.common.data.DataConstants; |
32 | +import org.thingsboard.server.common.data.Device; | ||
33 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
34 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
32 | import org.thingsboard.server.common.data.id.DeviceId; | 35 | import org.thingsboard.server.common.data.id.DeviceId; |
33 | import org.thingsboard.server.common.data.id.OtaPackageId; | 36 | import org.thingsboard.server.common.data.id.OtaPackageId; |
34 | import org.thingsboard.server.common.data.ota.OtaPackageType; | 37 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
@@ -41,17 +44,21 @@ import org.thingsboard.server.common.transport.TransportService; | @@ -41,17 +44,21 @@ import org.thingsboard.server.common.transport.TransportService; | ||
41 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 44 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
42 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 45 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
43 | import org.thingsboard.server.common.transport.auth.SessionInfoCreator; | 46 | import org.thingsboard.server.common.transport.auth.SessionInfoCreator; |
47 | +import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | ||
44 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; | 48 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
49 | +import org.thingsboard.server.common.transport.service.DefaultTransportService; | ||
50 | +import org.thingsboard.server.common.transport.service.SessionMetaData; | ||
45 | import org.thingsboard.server.common.transport.util.SslUtil; | 51 | import org.thingsboard.server.common.transport.util.SslUtil; |
46 | import org.thingsboard.server.gen.transport.TransportProtos; | 52 | import org.thingsboard.server.gen.transport.TransportProtos; |
47 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 53 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
48 | -import org.thingsboard.server.queue.scheduler.SchedulerComponent; | ||
49 | import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; | 54 | import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; |
50 | -import org.thingsboard.server.transport.tcp.session.DeviceSessionCtx; | 55 | +import org.thingsboard.server.transport.tcp.session.TcpDeviceSessionCtx; |
51 | import org.thingsboard.server.transport.tcp.session.TCPMessage; | 56 | import org.thingsboard.server.transport.tcp.session.TCPMessage; |
57 | +import org.thingsboard.server.transport.tcp.session.TcpGatewaySessionHandler; | ||
52 | import org.thingsboard.server.transport.tcp.util.ByteUtils; | 58 | import org.thingsboard.server.transport.tcp.util.ByteUtils; |
53 | 59 | ||
54 | import javax.net.ssl.SSLPeerUnverifiedException; | 60 | import javax.net.ssl.SSLPeerUnverifiedException; |
61 | +import java.io.IOException; | ||
55 | import java.io.UnsupportedEncodingException; | 62 | import java.io.UnsupportedEncodingException; |
56 | import java.net.InetSocketAddress; | 63 | import java.net.InetSocketAddress; |
57 | import java.security.cert.Certificate; | 64 | import java.security.cert.Certificate; |
@@ -62,13 +69,13 @@ import java.util.UUID; | @@ -62,13 +69,13 @@ import java.util.UUID; | ||
62 | import java.util.concurrent.ConcurrentHashMap; | 69 | import java.util.concurrent.ConcurrentHashMap; |
63 | import java.util.concurrent.ConcurrentMap; | 70 | import java.util.concurrent.ConcurrentMap; |
64 | import java.util.regex.Matcher; | 71 | import java.util.regex.Matcher; |
65 | -import java.util.regex.Pattern; | ||
66 | 72 | ||
67 | import static com.amazonaws.util.StringUtils.UTF8; | 73 | import static com.amazonaws.util.StringUtils.UTF8; |
68 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; | 74 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; |
69 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; | 75 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; |
70 | -import static io.netty.handler.codec.mqtt.MqttMessageType.*; | ||
71 | -import static io.netty.handler.codec.mqtt.MqttQoS.*; | 76 | +import static io.netty.handler.codec.mqtt.MqttMessageType.PUBACK; |
77 | +import static io.netty.handler.codec.mqtt.MqttMessageType.SUBACK; | ||
78 | +import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; | ||
72 | import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; | 79 | import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; |
73 | import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; | 80 | import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; |
74 | 81 | ||
@@ -82,14 +89,14 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -82,14 +89,14 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
82 | private final UUID sessionId; | 89 | private final UUID sessionId; |
83 | private final TcpTransportContext context; | 90 | private final TcpTransportContext context; |
84 | private final TransportService transportService; | 91 | private final TransportService transportService; |
85 | - private final SchedulerComponent scheduler; | ||
86 | private final SslHandler sslHandler; | 92 | private final SslHandler sslHandler; |
87 | 93 | ||
88 | 94 | ||
89 | /**需要处理的消息队列,例如:需要下发给设备的,设备上传的。*/ | 95 | /**需要处理的消息队列,例如:需要下发给设备的,设备上传的。*/ |
90 | - final DeviceSessionCtx deviceSessionCtx; | 96 | + final TcpDeviceSessionCtx deviceSessionCtx; |
91 | volatile InetSocketAddress address; | 97 | volatile InetSocketAddress address; |
92 | 98 | ||
99 | + volatile TcpGatewaySessionHandler gatewaySessionHandler; | ||
93 | private final ConcurrentHashMap<String, String> otaPackSessions; | 100 | private final ConcurrentHashMap<String, String> otaPackSessions; |
94 | private final ConcurrentHashMap<String, Integer> chunkSizes; | 101 | private final ConcurrentHashMap<String, Integer> chunkSizes; |
95 | private final ConcurrentMap<Integer, TransportProtos.ToDeviceRpcRequestMsg> rpcAwaitingAck; | 102 | private final ConcurrentMap<Integer, TransportProtos.ToDeviceRpcRequestMsg> rpcAwaitingAck; |
@@ -100,9 +107,8 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -100,9 +107,8 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
100 | this.sessionId = UUID.randomUUID(); | 107 | this.sessionId = UUID.randomUUID(); |
101 | this.context = context; | 108 | this.context = context; |
102 | this.transportService = context.getTransportService(); | 109 | this.transportService = context.getTransportService(); |
103 | - this.scheduler = context.getScheduler(); | ||
104 | this.sslHandler = sslHandler; | 110 | this.sslHandler = sslHandler; |
105 | - this.deviceSessionCtx = new DeviceSessionCtx(sessionId, context); | 111 | + this.deviceSessionCtx = new TcpDeviceSessionCtx(sessionId, context); |
106 | this.otaPackSessions = new ConcurrentHashMap<>(); | 112 | this.otaPackSessions = new ConcurrentHashMap<>(); |
107 | this.chunkSizes = new ConcurrentHashMap<>(); | 113 | this.chunkSizes = new ConcurrentHashMap<>(); |
108 | this.rpcAwaitingAck = new ConcurrentHashMap<>(); | 114 | this.rpcAwaitingAck = new ConcurrentHashMap<>(); |
@@ -197,7 +203,38 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -197,7 +203,38 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
197 | if (!checkConnected(ctx, tcpMessage)) { | 203 | if (!checkConnected(ctx, tcpMessage)) { |
198 | return; | 204 | return; |
199 | } | 205 | } |
206 | + log.error("【{}】设备【{}】收到数据【{}】", sessionId,deviceSessionCtx.getDeviceId(), tcpMessage.getMessage()); | ||
207 | + if (gatewaySessionHandler != null) { | ||
208 | + processGatewayDeviceMsg(ctx, tcpMessage); | ||
209 | + transportService.reportActivity(deviceSessionCtx.getSessionInfo()); | ||
210 | + } else { | ||
211 | + processDirectDeviceMsg(ctx,tcpMessage); | ||
212 | + } | ||
213 | + } | ||
200 | 214 | ||
215 | + | ||
216 | + private void processGatewayDeviceMsg(ChannelHandlerContext ctx, TCPMessage tcpMessage) { | ||
217 | + log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage.getMessage()); | ||
218 | + try { | ||
219 | + String topicName = tcpMessage.getTopic(); | ||
220 | + if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { | ||
221 | + gatewaySessionHandler.onDeviceAttributes(tcpMessage); | ||
222 | + } else if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) { | ||
223 | + gatewaySessionHandler.onDeviceTelemetry(tcpMessage); | ||
224 | + | ||
225 | + } else if (deviceSessionCtx.isToDeviceRpcResponseTopic(topicName)) { | ||
226 | + | ||
227 | + } else { | ||
228 | + transportService.reportActivity(deviceSessionCtx.getSessionInfo()); | ||
229 | + pushDeviceMsg(ctx,tcpMessage); | ||
230 | + } | ||
231 | + } catch (AdaptorException e) { | ||
232 | + log.debug("[{}] Failed to process publish msg [{}][{}]", sessionId, tcpMessage, e); | ||
233 | + ctx.close(); | ||
234 | + } | ||
235 | + } | ||
236 | + | ||
237 | + private void processDirectDeviceMsg(ChannelHandlerContext ctx, TCPMessage tcpMessage) { | ||
201 | log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage.getMessage()); | 238 | log.trace("[{}][{}] Processing publish msg [{}]!", sessionId, deviceSessionCtx.getDeviceId(), tcpMessage.getMessage()); |
202 | try { | 239 | try { |
203 | String topicName = tcpMessage.getTopic(); | 240 | String topicName = tcpMessage.getTopic(); |
@@ -279,7 +316,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -279,7 +316,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
279 | // attrReqTopicType = TopicType.V2; | 316 | // attrReqTopicType = TopicType.V2; |
280 | } else { | 317 | } else { |
281 | transportService.reportActivity(deviceSessionCtx.getSessionInfo()); | 318 | transportService.reportActivity(deviceSessionCtx.getSessionInfo()); |
282 | - pushDeviceMsg(tcpMessage.getMessage()); | 319 | + pushDeviceMsg(ctx,tcpMessage); |
283 | } | 320 | } |
284 | } catch (AdaptorException e) { | 321 | } catch (AdaptorException e) { |
285 | log.debug("[{}] Failed to process publish msg [{}][{}]", sessionId, tcpMessage, e); | 322 | log.debug("[{}] Failed to process publish msg [{}][{}]", sessionId, tcpMessage, e); |
@@ -322,18 +359,16 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -322,18 +359,16 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
322 | } | 359 | } |
323 | } | 360 | } |
324 | 361 | ||
325 | - private void ack(ChannelHandlerContext ctx, int msgId) { | ||
326 | - if (msgId > 0) { | ||
327 | - ctx.writeAndFlush(createMqttPubAckMsg(msgId)); | ||
328 | - } | ||
329 | - } | ||
330 | 362 | ||
331 | - private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { | 363 | + |
364 | + private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final String msgId, final TCPMessage msg) { | ||
332 | return new TransportServiceCallback<>() { | 365 | return new TransportServiceCallback<>() { |
333 | @Override | 366 | @Override |
334 | public void onSuccess(Void dummy) { | 367 | public void onSuccess(Void dummy) { |
335 | log.trace("[{}] Published msg: {}", sessionId, msg); | 368 | log.trace("[{}] Published msg: {}", sessionId, msg); |
336 | - ack(ctx, msgId); | 369 | + if(StringUtils.isNotEmpty(msgId)){ |
370 | + pushDeviceMsg(ctx,msg); | ||
371 | + } | ||
337 | } | 372 | } |
338 | 373 | ||
339 | @Override | 374 | @Override |
@@ -382,7 +417,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -382,7 +417,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
382 | 417 | ||
383 | private void sendOtaPackage(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, OtaPackageType type) { | 418 | private void sendOtaPackage(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, OtaPackageType type) { |
384 | log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); | 419 | log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); |
385 | - ack(ctx, msgId); | 420 | + pushDeviceMsg(ctx,new TCPMessage(requestId)); |
386 | try { | 421 | try { |
387 | byte[] firmwareChunk = context.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk); | 422 | byte[] firmwareChunk = context.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk); |
388 | deviceSessionCtx.getPayloadAdaptor() | 423 | deviceSessionCtx.getPayloadAdaptor() |
@@ -521,7 +556,23 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -521,7 +556,23 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
521 | } | 556 | } |
522 | } | 557 | } |
523 | 558 | ||
524 | - | 559 | + private void checkGatewaySession(SessionMetaData sessionMetaData) { |
560 | + TransportDeviceInfo device = deviceSessionCtx.getDeviceInfo(); | ||
561 | + try { | ||
562 | + JsonNode infoNode = context.getMapper().readTree(device.getAdditionalInfo()); | ||
563 | + if (infoNode != null) { | ||
564 | + JsonNode gatewayNode = infoNode.get("gateway"); | ||
565 | + if (gatewayNode != null && gatewayNode.asBoolean()) { | ||
566 | + gatewaySessionHandler = new TcpGatewaySessionHandler(deviceSessionCtx, sessionId); | ||
567 | + if (infoNode.has(DefaultTransportService.OVERWRITE_ACTIVITY_TIME) && infoNode.get(DefaultTransportService.OVERWRITE_ACTIVITY_TIME).isBoolean()) { | ||
568 | + sessionMetaData.setOverwriteActivityTime(infoNode.get(DefaultTransportService.OVERWRITE_ACTIVITY_TIME).asBoolean()); | ||
569 | + } | ||
570 | + } | ||
571 | + } | ||
572 | + } catch (IOException e) { | ||
573 | + log.trace("[{}][{}] Failed to fetch device additional info", sessionId, device.getDeviceName(), e); | ||
574 | + } | ||
575 | + } | ||
525 | 576 | ||
526 | @Override | 577 | @Override |
527 | public void operationComplete(Future<? super Void> future) throws Exception { | 578 | public void operationComplete(Future<? super Void> future) throws Exception { |
@@ -534,6 +585,9 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -534,6 +585,9 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
534 | log.debug("[{}] Client disconnected!", sessionId); | 585 | log.debug("[{}] Client disconnected!", sessionId); |
535 | transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); | 586 | transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); |
536 | transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); | 587 | transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); |
588 | + if (gatewaySessionHandler != null) { | ||
589 | + gatewaySessionHandler.onGatewayDisconnect(); | ||
590 | + } | ||
537 | deviceSessionCtx.setDisconnected(); | 591 | deviceSessionCtx.setDisconnected(); |
538 | } | 592 | } |
539 | deviceSessionCtx.release(); | 593 | deviceSessionCtx.release(); |
@@ -553,7 +607,8 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -553,7 +607,8 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
553 | transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_OPEN, new TransportServiceCallback<Void>() { | 607 | transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_OPEN, new TransportServiceCallback<Void>() { |
554 | @Override | 608 | @Override |
555 | public void onSuccess(Void msg) { | 609 | public void onSuccess(Void msg) { |
556 | - transportService.registerAsyncSession(deviceSessionCtx.getSessionInfo(), TcpTransportHandler.this); | 610 | + SessionMetaData sessionMetaData = transportService.registerAsyncSession(deviceSessionCtx.getSessionInfo(), TcpTransportHandler.this); |
611 | + checkGatewaySession(sessionMetaData); | ||
557 | ctx.writeAndFlush(createTcpConnAckMsg(CONNECTION_ACCEPTED)); | 612 | ctx.writeAndFlush(createTcpConnAckMsg(CONNECTION_ACCEPTED)); |
558 | deviceSessionCtx.setConnected(true); | 613 | deviceSessionCtx.setConnected(true); |
559 | log.debug("[{}] Client connected!", sessionId); | 614 | log.debug("[{}] Client connected!", sessionId); |
@@ -608,7 +663,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -608,7 +663,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
608 | 663 | ||
609 | @Override | 664 | @Override |
610 | public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | 665 | public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { |
611 | - log.error("【{}】下发RPC命令【】给设备", sessionId,rpcRequest.getParams()); | 666 | + log.error("【{}】下发RPC命令【{}】给设备", sessionId,rpcRequest.getParams()); |
612 | TcpTransportAdaptor adaptor = deviceSessionCtx.getPayloadAdaptor(); | 667 | TcpTransportAdaptor adaptor = deviceSessionCtx.getPayloadAdaptor(); |
613 | try { | 668 | try { |
614 | adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(payload -> { | 669 | adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(payload -> { |
@@ -622,7 +677,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -622,7 +677,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
622 | // } | 677 | // } |
623 | // }, Math.max(0, Math.min(deviceSessionCtx.getContext().getTimeout(), rpcRequest.getExpirationTime() - System.currentTimeMillis())), TimeUnit.MILLISECONDS); | 678 | // }, Math.max(0, Math.min(deviceSessionCtx.getContext().getTimeout(), rpcRequest.getExpirationTime() - System.currentTimeMillis())), TimeUnit.MILLISECONDS); |
624 | // } | 679 | // } |
625 | - var cf = pushDeviceMsg(payload.getMessage()); | 680 | + var cf = pushDeviceMsg(deviceSessionCtx.getChannel(), payload); |
626 | cf.addListener(result -> { | 681 | cf.addListener(result -> { |
627 | if (result.cause() == null) { | 682 | if (result.cause() == null) { |
628 | // if (!isAckExpected(payload)) { | 683 | // if (!isAckExpected(payload)) { |
@@ -658,11 +713,12 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -658,11 +713,12 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
658 | 713 | ||
659 | /** | 714 | /** |
660 | * 往设备推送消息 | 715 | * 往设备推送消息 |
661 | - * @param message | 716 | + * @param tcp |
662 | * @return | 717 | * @return |
663 | */ | 718 | */ |
664 | - private ChannelFuture pushDeviceMsg(String message) { | 719 | + private ChannelFuture pushDeviceMsg(ChannelHandlerContext ctx,TCPMessage tcp) { |
665 | try { | 720 | try { |
721 | + String message = tcp.getMessage(); | ||
666 | byte[] payloadInBytes ; | 722 | byte[] payloadInBytes ; |
667 | if(deviceSessionCtx.getPayloadType().equals(TcpDataTypeEnum.HEX)){ | 723 | if(deviceSessionCtx.getPayloadType().equals(TcpDataTypeEnum.HEX)){ |
668 | payloadInBytes = ByteUtils.hexStr2Bytes(message); | 724 | payloadInBytes = ByteUtils.hexStr2Bytes(message); |
@@ -674,7 +730,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | @@ -674,7 +730,7 @@ public class TcpTransportHandler extends ChannelInboundHandlerAdapter implements | ||
674 | // payload.writeBytes(payloadInBytes); | 730 | // payload.writeBytes(payloadInBytes); |
675 | ByteBuf payload = Unpooled.copiedBuffer(payloadInBytes); | 731 | ByteBuf payload = Unpooled.copiedBuffer(payloadInBytes); |
676 | 732 | ||
677 | - return deviceSessionCtx.getChannel().writeAndFlush(payload); | 733 | + return ctx.writeAndFlush(payload); |
678 | } catch (UnsupportedEncodingException e) { | 734 | } catch (UnsupportedEncodingException e) { |
679 | log.error(e.getMessage(),e); | 735 | log.error(e.getMessage(),e); |
680 | throw new RuntimeException(e); | 736 | throw new RuntimeException(e); |
common/transport/tcp/src/main/java/org/thingsboard/server/transport/tcp/adaptors/JsonTcpAdaptor.java
1 | /** | 1 | /** |
2 | * Copyright © 2016-2022 The Thingsboard Authors | 2 | * Copyright © 2016-2022 The Thingsboard Authors |
3 | - * | 3 | + * <p> |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at | 6 | * You may obtain a copy of the License at |
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | 7 | + * <p> |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * <p> | ||
10 | * Unless required by applicable law or agreed to in writing, software | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
@@ -34,8 +34,8 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter; | @@ -34,8 +34,8 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter; | ||
34 | import org.thingsboard.server.common.yunteng.script.YtScriptInvokeService; | 34 | import org.thingsboard.server.common.yunteng.script.YtScriptInvokeService; |
35 | import org.thingsboard.server.common.yunteng.script.YtScriptType; | 35 | import org.thingsboard.server.common.yunteng.script.YtScriptType; |
36 | import org.thingsboard.server.gen.transport.TransportProtos; | 36 | import org.thingsboard.server.gen.transport.TransportProtos; |
37 | -import org.thingsboard.server.transport.tcp.session.DeviceSessionCtx; | ||
38 | import org.thingsboard.server.transport.tcp.session.TCPMessage; | 37 | import org.thingsboard.server.transport.tcp.session.TCPMessage; |
38 | +import org.thingsboard.server.transport.tcp.session.TcpDeviceWareSessionContext; | ||
39 | 39 | ||
40 | import java.io.UnsupportedEncodingException; | 40 | import java.io.UnsupportedEncodingException; |
41 | import java.nio.charset.Charset; | 41 | import java.nio.charset.Charset; |
@@ -55,8 +55,9 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -55,8 +55,9 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
55 | @Autowired | 55 | @Autowired |
56 | private YtScriptInvokeService jsEngine; | 56 | private YtScriptInvokeService jsEngine; |
57 | private static final JsonParser parser = new JsonParser(); | 57 | private static final JsonParser parser = new JsonParser(); |
58 | + | ||
58 | @Override | 59 | @Override |
59 | - public TransportProtos.PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, String inbound) throws AdaptorException { | 60 | + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(TcpDeviceWareSessionContext ctx, String inbound) throws AdaptorException { |
60 | try { | 61 | try { |
61 | JsonElement payload = validatePayload(ctx, inbound, false); | 62 | JsonElement payload = validatePayload(ctx, inbound, false); |
62 | return JsonConverter.convertToTelemetryProto(payload); | 63 | return JsonConverter.convertToTelemetryProto(payload); |
@@ -76,7 +77,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -76,7 +77,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
76 | } | 77 | } |
77 | 78 | ||
78 | @Override | 79 | @Override |
79 | - public TransportProtos.PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { | 80 | + public TransportProtos.PostAttributeMsg convertToPostAttributes(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
80 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); | 81 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
81 | try { | 82 | try { |
82 | return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); | 83 | return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); |
@@ -87,7 +88,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -87,7 +88,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
87 | } | 88 | } |
88 | 89 | ||
89 | @Override | 90 | @Override |
90 | - public TransportProtos.ClaimDeviceMsg convertToClaimDevice(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { | 91 | + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
91 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true); | 92 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true); |
92 | try { | 93 | try { |
93 | return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload); | 94 | return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload); |
@@ -98,7 +99,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -98,7 +99,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
98 | } | 99 | } |
99 | 100 | ||
100 | @Override | 101 | @Override |
101 | - public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException { | 102 | + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
102 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); | 103 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
103 | try { | 104 | try { |
104 | return JsonConverter.convertToProvisionRequestMsg(payload); | 105 | return JsonConverter.convertToProvisionRequestMsg(payload); |
@@ -108,72 +109,72 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -108,72 +109,72 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
108 | } | 109 | } |
109 | 110 | ||
110 | @Override | 111 | @Override |
111 | - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException { | 112 | + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException { |
112 | return processGetAttributeRequestMsg(inbound, topicBase); | 113 | return processGetAttributeRequestMsg(inbound, topicBase); |
113 | } | 114 | } |
114 | 115 | ||
115 | @Override | 116 | @Override |
116 | - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(DeviceSessionCtx ctx, String inbound, String topicBase) throws AdaptorException { | 117 | + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(TcpDeviceWareSessionContext ctx, String inbound, String topicBase) throws AdaptorException { |
117 | return processToDeviceRpcResponseMsg(inbound, topicBase); | 118 | return processToDeviceRpcResponseMsg(inbound, topicBase); |
118 | } | 119 | } |
119 | 120 | ||
120 | @Override | 121 | @Override |
121 | - public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(DeviceSessionCtx ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException { | 122 | + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException { |
122 | return processToServerRpcRequestMsg(ctx, inbound, topicBase); | 123 | return processToServerRpcRequestMsg(ctx, inbound, topicBase); |
123 | } | 124 | } |
124 | 125 | ||
125 | @Override | 126 | @Override |
126 | - public Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException { | 127 | + public Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException { |
127 | return processConvertFromAttributeResponseMsg(ctx, responseMsg, topicBase); | 128 | return processConvertFromAttributeResponseMsg(ctx, responseMsg, topicBase); |
128 | } | 129 | } |
129 | 130 | ||
130 | @Override | 131 | @Override |
131 | - public Optional<TCPMessage> convertToGatewayPublish(DeviceSessionCtx ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 132 | + public Optional<TCPMessage> convertToGatewayPublish(TcpDeviceWareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
132 | return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg); | 133 | return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg); |
133 | } | 134 | } |
134 | 135 | ||
135 | @Override | 136 | @Override |
136 | - public Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg, String topic) { | 137 | + public Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg, String topic) { |
137 | return Optional.of(createTcpMessage(ctx, JsonConverter.toJson(notificationMsg))); | 138 | return Optional.of(createTcpMessage(ctx, JsonConverter.toJson(notificationMsg))); |
138 | } | 139 | } |
139 | 140 | ||
140 | @Override | 141 | @Override |
141 | - public Optional<TCPMessage> convertToGatewayPublish(DeviceSessionCtx ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { | 142 | + public Optional<TCPMessage> convertToGatewayPublish(TcpDeviceWareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { |
142 | JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg); | 143 | JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg); |
143 | return Optional.of(createTcpMessage(ctx, result)); | 144 | return Optional.of(createTcpMessage(ctx, result)); |
144 | } | 145 | } |
145 | 146 | ||
146 | @Override | 147 | @Override |
147 | - public Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws UnsupportedEncodingException { | ||
148 | - byte[] result = null; | 148 | + public Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws UnsupportedEncodingException { |
149 | + byte[] result = null; | ||
149 | String payload = rpcRequest.getParams();//methodThingskit | 150 | String payload = rpcRequest.getParams();//methodThingskit |
150 | // if(ctx.getPayloadType().equals(TcpDataTypeEnum.ASCII)){ | 151 | // if(ctx.getPayloadType().equals(TcpDataTypeEnum.ASCII)){ |
151 | // }else{ | 152 | // }else{ |
152 | // result= ByteUtils.hexToBytes(payload); | 153 | // result= ByteUtils.hexToBytes(payload); |
153 | // } | 154 | // } |
154 | - if(!payload.startsWith("{") && !payload.endsWith("}")){ | ||
155 | - payload = payload.substring(1,payload.length()-1); | 155 | + if (!payload.startsWith("{") && !payload.endsWith("}")) { |
156 | + payload = payload.replace("\"","");; | ||
156 | } | 157 | } |
157 | - return Optional.of(createTcpMessage(ctx,payload)); | 158 | + return Optional.of(createTcpMessage(ctx, payload)); |
158 | } | 159 | } |
159 | 160 | ||
160 | @Override | 161 | @Override |
161 | - public Optional<TCPMessage> convertToGatewayPublish(DeviceSessionCtx ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | 162 | + public Optional<TCPMessage> convertToGatewayPublish(TcpDeviceWareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { |
162 | return Optional.of(createTcpMessage(ctx, JsonConverter.toGatewayJson(deviceName, rpcRequest))); | 163 | return Optional.of(createTcpMessage(ctx, JsonConverter.toGatewayJson(deviceName, rpcRequest))); |
163 | } | 164 | } |
164 | 165 | ||
165 | @Override | 166 | @Override |
166 | - public Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse, String topicBase) { | 167 | + public Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse, String topicBase) { |
167 | return Optional.of(createTcpMessage(ctx, JsonConverter.toJson(rpcResponse))); | 168 | return Optional.of(createTcpMessage(ctx, JsonConverter.toJson(rpcResponse))); |
168 | } | 169 | } |
169 | 170 | ||
170 | @Override | 171 | @Override |
171 | - public Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, TransportProtos.ProvisionDeviceResponseMsg provisionResponse) { | ||
172 | - return Optional.of(createTcpMessage(ctx,JsonConverter.toJson(provisionResponse))); | 172 | + public Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, TransportProtos.ProvisionDeviceResponseMsg provisionResponse) { |
173 | + return Optional.of(createTcpMessage(ctx, JsonConverter.toJson(provisionResponse))); | ||
173 | } | 174 | } |
174 | 175 | ||
175 | @Override | 176 | @Override |
176 | - public Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) { | 177 | + public Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) { |
177 | return Optional.of(null); | 178 | return Optional.of(null); |
178 | } | 179 | } |
179 | 180 | ||
@@ -222,7 +223,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -222,7 +223,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
222 | return null; | 223 | return null; |
223 | } | 224 | } |
224 | 225 | ||
225 | - private TransportProtos.ToServerRpcRequestMsg processToServerRpcRequestMsg(DeviceSessionCtx ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException { | 226 | + private TransportProtos.ToServerRpcRequestMsg processToServerRpcRequestMsg(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException { |
226 | String topicName = inbound.variableHeader().topicName(); | 227 | String topicName = inbound.variableHeader().topicName(); |
227 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); | 228 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
228 | try { | 229 | try { |
@@ -234,7 +235,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -234,7 +235,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
234 | } | 235 | } |
235 | } | 236 | } |
236 | 237 | ||
237 | - private Optional<TCPMessage> processConvertFromAttributeResponseMsg(DeviceSessionCtx ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException { | 238 | + private Optional<TCPMessage> processConvertFromAttributeResponseMsg(TcpDeviceWareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException { |
238 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 239 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
239 | throw new AdaptorException(responseMsg.getError()); | 240 | throw new AdaptorException(responseMsg.getError()); |
240 | } else { | 241 | } else { |
@@ -247,17 +248,16 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -247,17 +248,16 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
247 | } | 248 | } |
248 | } | 249 | } |
249 | 250 | ||
250 | - private Optional<TCPMessage> processConvertFromGatewayAttributeResponseMsg(DeviceSessionCtx ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 251 | + private Optional<TCPMessage> processConvertFromGatewayAttributeResponseMsg(TcpDeviceWareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
251 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 252 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
252 | throw new AdaptorException(responseMsg.getError()); | 253 | throw new AdaptorException(responseMsg.getError()); |
253 | } else { | 254 | } else { |
254 | JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); | 255 | JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); |
255 | - return Optional.of(createTcpMessage(ctx, result)); | 256 | + return Optional.of(createTcpMessage(ctx, result)); |
256 | } | 257 | } |
257 | } | 258 | } |
258 | 259 | ||
259 | 260 | ||
260 | - | ||
261 | private Set<String> toStringSet(JsonElement requestBody, String name) { | 261 | private Set<String> toStringSet(JsonElement requestBody, String name) { |
262 | JsonElement element = requestBody.getAsJsonObject().get(name); | 262 | JsonElement element = requestBody.getAsJsonObject().get(name); |
263 | if (element != null) { | 263 | if (element != null) { |
@@ -278,7 +278,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -278,7 +278,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
278 | return payload; | 278 | return payload; |
279 | } | 279 | } |
280 | 280 | ||
281 | - private JsonElement validatePayload(DeviceSessionCtx session, String payload, boolean isEmptyPayloadAllowed) throws AdaptorException, ExecutionException, InterruptedException { | 281 | + private JsonElement validatePayload(TcpDeviceWareSessionContext session, String payload, boolean isEmptyPayloadAllowed) throws AdaptorException, ExecutionException, InterruptedException { |
282 | if (payload == null) { | 282 | if (payload == null) { |
283 | log.debug("[{}] Payload is empty!", session.getSessionId()); | 283 | log.debug("[{}] Payload is empty!", session.getSessionId()); |
284 | if (!isEmptyPayloadAllowed) { | 284 | if (!isEmptyPayloadAllowed) { |
@@ -305,7 +305,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -305,7 +305,7 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
305 | } | 305 | } |
306 | 306 | ||
307 | 307 | ||
308 | - protected TCPMessage createTcpMessage(DeviceSessionCtx ctx, JsonElement json) { | 308 | + protected TCPMessage createTcpMessage(TcpDeviceWareSessionContext ctx, JsonElement json) { |
309 | // TCPMessage msg = new TCPMessage(MqttMessageType.PUBLISH,); | 309 | // TCPMessage msg = new TCPMessage(MqttMessageType.PUBLISH,); |
310 | //// new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); | 310 | //// new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); |
311 | // MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); | 311 | // MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); |
@@ -315,11 +315,11 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | @@ -315,11 +315,11 @@ public class JsonTcpAdaptor implements TcpTransportAdaptor { | ||
315 | return null; | 315 | return null; |
316 | } | 316 | } |
317 | 317 | ||
318 | - protected TCPMessage createTcpMessage(DeviceSessionCtx ctx, String payload) { | 318 | + protected TCPMessage createTcpMessage(TcpDeviceWareSessionContext ctx, String payload) { |
319 | TCPMessage message = new TCPMessage(payload); | 319 | TCPMessage message = new TCPMessage(payload); |
320 | - message.setRequestId(payload.substring(0,4)); | ||
321 | - message.setTopic(payload.substring(2,4)); | ||
322 | - message.setDeviceCode(payload.substring(0,2)); | 320 | + message.setRequestId(payload.substring(0, 4)); |
321 | + message.setTopic(payload.substring(2, 4)); | ||
322 | + message.setDeviceCode(payload.substring(0, 2)); | ||
323 | return message; | 323 | return message; |
324 | } | 324 | } |
325 | 325 |
@@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; | @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; | ||
21 | import org.thingsboard.server.common.data.yunteng.enums.TcpDataTypeEnum; | 21 | import org.thingsboard.server.common.data.yunteng.enums.TcpDataTypeEnum; |
22 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 22 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
23 | import org.thingsboard.server.gen.transport.TransportProtos.*; | 23 | import org.thingsboard.server.gen.transport.TransportProtos.*; |
24 | -import org.thingsboard.server.transport.tcp.session.DeviceSessionCtx; | 24 | +import org.thingsboard.server.transport.tcp.session.TcpDeviceWareSessionContext; |
25 | import org.thingsboard.server.transport.tcp.session.TCPMessage; | 25 | import org.thingsboard.server.transport.tcp.session.TCPMessage; |
26 | 26 | ||
27 | import java.io.UnsupportedEncodingException; | 27 | import java.io.UnsupportedEncodingException; |
@@ -32,44 +32,46 @@ import java.util.UUID; | @@ -32,44 +32,46 @@ import java.util.UUID; | ||
32 | import java.util.concurrent.ExecutionException; | 32 | import java.util.concurrent.ExecutionException; |
33 | 33 | ||
34 | /** | 34 | /** |
35 | - * @author Andrew Shvayka | 35 | + * 将收到的数据流转换为接口需要的数据格式 |
36 | + * 1、基于解析脚本将ByteBuf转JSON对象。 | ||
37 | + * 2、将JSON对象转PROTOBUF对象。 | ||
36 | */ | 38 | */ |
37 | public interface TcpTransportAdaptor { | 39 | public interface TcpTransportAdaptor { |
38 | static char[] HEX_VOCABLE = {'0', '1', '2', '3', '4', '5', '6', '7', | 40 | static char[] HEX_VOCABLE = {'0', '1', '2', '3', '4', '5', '6', '7', |
39 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; | 41 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
40 | static final Charset UTF8 = StandardCharsets.UTF_8; | 42 | static final Charset UTF8 = StandardCharsets.UTF_8; |
41 | - PostTelemetryMsg convertToPostTelemetry(DeviceSessionCtx ctx, String inbound) throws AdaptorException; | 43 | + PostTelemetryMsg convertToPostTelemetry(TcpDeviceWareSessionContext ctx, String inbound) throws AdaptorException; |
42 | 44 | ||
43 | UUID getJsScriptEngineFunctionId(String scriptBody, String... argNames) throws ExecutionException, InterruptedException; | 45 | UUID getJsScriptEngineFunctionId(String scriptBody, String... argNames) throws ExecutionException, InterruptedException; |
44 | - PostAttributeMsg convertToPostAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; | 46 | + PostAttributeMsg convertToPostAttributes(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; |
45 | 47 | ||
46 | - GetAttributeRequestMsg convertToGetAttributes(DeviceSessionCtx ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException; | 48 | + GetAttributeRequestMsg convertToGetAttributes(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound, String topicBase) throws AdaptorException; |
47 | 49 | ||
48 | - ToDeviceRpcResponseMsg convertToDeviceRpcResponse(DeviceSessionCtx ctx, String mqttMsg, String topicBase) throws AdaptorException; | 50 | + ToDeviceRpcResponseMsg convertToDeviceRpcResponse(TcpDeviceWareSessionContext ctx, String mqttMsg, String topicBase) throws AdaptorException; |
49 | 51 | ||
50 | - ToServerRpcRequestMsg convertToServerRpcRequest(DeviceSessionCtx ctx, MqttPublishMessage mqttMsg, String topicBase) throws AdaptorException; | 52 | + ToServerRpcRequestMsg convertToServerRpcRequest(TcpDeviceWareSessionContext ctx, MqttPublishMessage mqttMsg, String topicBase) throws AdaptorException; |
51 | 53 | ||
52 | - ClaimDeviceMsg convertToClaimDevice(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; | 54 | + ClaimDeviceMsg convertToClaimDevice(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; |
53 | 55 | ||
54 | - Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException; | 56 | + Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, GetAttributeResponseMsg responseMsg, String topicBase) throws AdaptorException; |
55 | 57 | ||
56 | - Optional<TCPMessage> convertToGatewayPublish(DeviceSessionCtx ctx, String deviceName, GetAttributeResponseMsg responseMsg) throws AdaptorException; | 58 | + Optional<TCPMessage> convertToGatewayPublish(TcpDeviceWareSessionContext ctx, String deviceName, GetAttributeResponseMsg responseMsg) throws AdaptorException; |
57 | 59 | ||
58 | - Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, AttributeUpdateNotificationMsg notificationMsg, String topic) throws AdaptorException; | 60 | + Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, AttributeUpdateNotificationMsg notificationMsg, String topic) throws AdaptorException; |
59 | 61 | ||
60 | - Optional<TCPMessage> convertToGatewayPublish(DeviceSessionCtx ctx, String deviceName, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; | 62 | + Optional<TCPMessage> convertToGatewayPublish(TcpDeviceWareSessionContext ctx, String deviceName, AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; |
61 | 63 | ||
62 | - Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException, UnsupportedEncodingException; | 64 | + Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException, UnsupportedEncodingException; |
63 | 65 | ||
64 | - Optional<TCPMessage> convertToGatewayPublish(DeviceSessionCtx ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; | 66 | + Optional<TCPMessage> convertToGatewayPublish(TcpDeviceWareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; |
65 | 67 | ||
66 | - Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, ToServerRpcResponseMsg rpcResponse, String topicBase) throws AdaptorException; | 68 | + Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, ToServerRpcResponseMsg rpcResponse, String topicBase) throws AdaptorException; |
67 | 69 | ||
68 | - ProvisionDeviceRequestMsg convertToProvisionRequestMsg(DeviceSessionCtx ctx, MqttPublishMessage inbound) throws AdaptorException; | 70 | + ProvisionDeviceRequestMsg convertToProvisionRequestMsg(TcpDeviceWareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; |
69 | 71 | ||
70 | - Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; | 72 | + Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; |
71 | 73 | ||
72 | - Optional<TCPMessage> convertToPublish(DeviceSessionCtx ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException; | 74 | + Optional<TCPMessage> convertToPublish(TcpDeviceWareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException; |
73 | public static byte[] toBytes(ByteBuf inbound) { | 75 | public static byte[] toBytes(ByteBuf inbound) { |
74 | byte[] bytes = new byte[inbound.readableBytes()]; | 76 | byte[] bytes = new byte[inbound.readableBytes()]; |
75 | int readerIndex = inbound.readerIndex(); | 77 | int readerIndex = inbound.readerIndex(); |
@@ -86,7 +88,7 @@ public interface TcpTransportAdaptor { | @@ -86,7 +88,7 @@ public interface TcpTransportAdaptor { | ||
86 | } | 88 | } |
87 | return sb.toString(); | 89 | return sb.toString(); |
88 | } | 90 | } |
89 | - default TCPMessage createTcpMessage(DeviceSessionCtx ctx, ByteBuf payload) { | 91 | + default TCPMessage createTcpMessage(TcpDeviceWareSessionContext ctx, ByteBuf payload) { |
90 | String payloadStr; | 92 | String payloadStr; |
91 | if(ctx.getPayloadType().equals(TcpDataTypeEnum.HEX)){ | 93 | if(ctx.getPayloadType().equals(TcpDataTypeEnum.HEX)){ |
92 | byte[] payloadBytes = toBytes(payload); | 94 | byte[] payloadBytes = toBytes(payload); |
1 | +/** | ||
2 | + * Copyright © 2016-2022 The Thingsboard Authors | ||
3 | + * <p> | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * <p> | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * <p> | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.transport.tcp.session; | ||
17 | + | ||
18 | +import io.netty.channel.ChannelHandlerContext; | ||
19 | +import io.netty.util.ReferenceCountUtil; | ||
20 | +import lombok.Getter; | ||
21 | +import lombok.Setter; | ||
22 | +import lombok.extern.slf4j.Slf4j; | ||
23 | +import org.thingsboard.server.transport.tcp.TcpTransportContext; | ||
24 | + | ||
25 | +import java.util.UUID; | ||
26 | +import java.util.concurrent.ConcurrentLinkedQueue; | ||
27 | +import java.util.concurrent.atomic.AtomicInteger; | ||
28 | +import java.util.concurrent.locks.Lock; | ||
29 | +import java.util.concurrent.locks.ReentrantLock; | ||
30 | +import java.util.function.Consumer; | ||
31 | + | ||
32 | +/** | ||
33 | + * @author Andrew Shvayka | ||
34 | + */ | ||
35 | +@Slf4j | ||
36 | +public class TcpDeviceSessionCtx extends TcpDeviceWareSessionContext { | ||
37 | + | ||
38 | + @Getter | ||
39 | + @Setter | ||
40 | + private ChannelHandlerContext channel; | ||
41 | + | ||
42 | + | ||
43 | + private final AtomicInteger msgIdSeq = new AtomicInteger(0); | ||
44 | + | ||
45 | + private final ConcurrentLinkedQueue<TCPMessage> msgQueue = new ConcurrentLinkedQueue<>(); | ||
46 | + | ||
47 | + @Getter | ||
48 | + private final Lock msgQueueProcessorLock = new ReentrantLock(); | ||
49 | + | ||
50 | + private final AtomicInteger msgQueueSize = new AtomicInteger(0); | ||
51 | + | ||
52 | + @Getter | ||
53 | + @Setter | ||
54 | + private boolean provisionOnly = false; | ||
55 | + | ||
56 | + public TcpDeviceSessionCtx(UUID sessionId, TcpTransportContext context) { | ||
57 | + super(sessionId,context); | ||
58 | + } | ||
59 | + | ||
60 | + public int nextMsgId() { | ||
61 | + return msgIdSeq.incrementAndGet(); | ||
62 | + } | ||
63 | + | ||
64 | + | ||
65 | + | ||
66 | + | ||
67 | + | ||
68 | + | ||
69 | + | ||
70 | + | ||
71 | + | ||
72 | + | ||
73 | + | ||
74 | + | ||
75 | + | ||
76 | + public void addToQueue(TCPMessage msg) { | ||
77 | + msgQueueSize.incrementAndGet(); | ||
78 | + ReferenceCountUtil.retain(msg); | ||
79 | + msgQueue.add(msg); | ||
80 | + } | ||
81 | + | ||
82 | + public void tryProcessQueuedMsgs(Consumer<TCPMessage> msgProcessor) { | ||
83 | + while (!msgQueue.isEmpty()) { | ||
84 | + if (msgQueueProcessorLock.tryLock()) { | ||
85 | + try { | ||
86 | + TCPMessage msg; | ||
87 | + while ((msg = msgQueue.poll()) != null) { | ||
88 | + try { | ||
89 | + msgQueueSize.decrementAndGet(); | ||
90 | + msgProcessor.accept(msg); | ||
91 | + } finally { | ||
92 | + ReferenceCountUtil.safeRelease(msg); | ||
93 | + } | ||
94 | + } | ||
95 | + } finally { | ||
96 | + msgQueueProcessorLock.unlock(); | ||
97 | + } | ||
98 | + } else { | ||
99 | + return; | ||
100 | + } | ||
101 | + } | ||
102 | + } | ||
103 | + | ||
104 | + public int getMsgQueueSize() { | ||
105 | + return msgQueueSize.get(); | ||
106 | + } | ||
107 | + | ||
108 | + public void release() { | ||
109 | + if (!msgQueue.isEmpty()) { | ||
110 | + log.warn("doDisconnect for device {} but unprocessed messages {} left in the msg queue", getDeviceId(), msgQueue.size()); | ||
111 | + msgQueue.forEach(ReferenceCountUtil::safeRelease); | ||
112 | + msgQueue.clear(); | ||
113 | + } | ||
114 | + } | ||
115 | + | ||
116 | + | ||
117 | +} |
common/transport/tcp/src/main/java/org/thingsboard/server/transport/tcp/session/TcpDeviceWareSessionContext.java
renamed from
common/transport/tcp/src/main/java/org/thingsboard/server/transport/tcp/session/DeviceSessionCtx.java
@@ -15,75 +15,56 @@ | @@ -15,75 +15,56 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.tcp.session; | 16 | package org.thingsboard.server.transport.tcp.session; |
17 | 17 | ||
18 | -import io.netty.channel.ChannelHandlerContext; | ||
19 | -import io.netty.util.ReferenceCountUtil; | 18 | +import com.fasterxml.jackson.core.JsonProcessingException; |
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | import lombok.Getter; | 20 | import lombok.Getter; |
21 | -import lombok.Setter; | ||
22 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
23 | import org.thingsboard.server.common.data.DeviceProfile; | 22 | import org.thingsboard.server.common.data.DeviceProfile; |
24 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | 23 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
25 | import org.thingsboard.server.common.data.device.profile.YtTcpDeviceProfileTransportConfiguration; | 24 | import org.thingsboard.server.common.data.device.profile.YtTcpDeviceProfileTransportConfiguration; |
25 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
26 | import org.thingsboard.server.common.data.yunteng.enums.TcpDataTypeEnum; | 26 | import org.thingsboard.server.common.data.yunteng.enums.TcpDataTypeEnum; |
27 | +import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | ||
27 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; | 28 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; |
28 | import org.thingsboard.server.gen.transport.TransportProtos; | 29 | import org.thingsboard.server.gen.transport.TransportProtos; |
29 | import org.thingsboard.server.transport.tcp.TcpTransportContext; | 30 | import org.thingsboard.server.transport.tcp.TcpTransportContext; |
30 | import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; | 31 | import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; |
31 | -import org.thingsboard.server.transport.tcp.util.ByteUtils; | ||
32 | 32 | ||
33 | import java.util.UUID; | 33 | import java.util.UUID; |
34 | -import java.util.concurrent.ConcurrentLinkedQueue; | ||
35 | import java.util.concurrent.ExecutionException; | 34 | import java.util.concurrent.ExecutionException; |
36 | -import java.util.concurrent.atomic.AtomicInteger; | ||
37 | -import java.util.concurrent.locks.Lock; | ||
38 | -import java.util.concurrent.locks.ReentrantLock; | ||
39 | -import java.util.function.Consumer; | ||
40 | 35 | ||
41 | /** | 36 | /** |
42 | * @author Andrew Shvayka | 37 | * @author Andrew Shvayka |
43 | */ | 38 | */ |
44 | @Slf4j | 39 | @Slf4j |
45 | -public class DeviceSessionCtx extends DeviceAwareSessionContext { | ||
46 | - | ||
47 | - @Getter | ||
48 | - @Setter | ||
49 | - private ChannelHandlerContext channel; | 40 | +public abstract class TcpDeviceWareSessionContext extends DeviceAwareSessionContext { |
50 | 41 | ||
51 | @Getter | 42 | @Getter |
52 | private final TcpTransportContext context; | 43 | private final TcpTransportContext context; |
53 | 44 | ||
54 | - private final AtomicInteger msgIdSeq = new AtomicInteger(0); | ||
55 | - | ||
56 | - private final ConcurrentLinkedQueue<TCPMessage> msgQueue = new ConcurrentLinkedQueue<>(); | ||
57 | - | ||
58 | - @Getter | ||
59 | - private final Lock msgQueueProcessorLock = new ReentrantLock(); | ||
60 | - | ||
61 | - private final AtomicInteger msgQueueSize = new AtomicInteger(0); | ||
62 | - | ||
63 | - @Getter | ||
64 | - @Setter | ||
65 | - private boolean provisionOnly = false; | ||
66 | - | ||
67 | private volatile String telemetryTopicFilter ; | 45 | private volatile String telemetryTopicFilter ; |
68 | private volatile String attributesTopicFilter; | 46 | private volatile String attributesTopicFilter; |
69 | private volatile String toDeviceRpcResponseTopicFilter; | 47 | private volatile String toDeviceRpcResponseTopicFilter; |
70 | @Getter | 48 | @Getter |
71 | private volatile TcpDataTypeEnum payloadType = TcpDataTypeEnum.HEX; | 49 | private volatile TcpDataTypeEnum payloadType = TcpDataTypeEnum.HEX; |
72 | 50 | ||
73 | - | ||
74 | private volatile TcpTransportAdaptor adaptor; | 51 | private volatile TcpTransportAdaptor adaptor; |
52 | + | ||
53 | + /**设备唯一标识符,例如:设备SN、设备地址码等。数据内携带标识符*/ | ||
54 | + @Getter | ||
55 | + private volatile String deviceCode = "55"; | ||
56 | + | ||
57 | + | ||
75 | @Getter | 58 | @Getter |
76 | private UUID scriptId; | 59 | private UUID scriptId; |
77 | 60 | ||
78 | - public DeviceSessionCtx(UUID sessionId, TcpTransportContext context) { | 61 | + public TcpDeviceWareSessionContext(UUID sessionId, TcpTransportContext context) { |
79 | super(sessionId); | 62 | super(sessionId); |
80 | this.context = context; | 63 | this.context = context; |
81 | this.adaptor = context.getJsonTcpAdaptor(); | 64 | this.adaptor = context.getJsonTcpAdaptor(); |
82 | } | 65 | } |
83 | 66 | ||
84 | - public int nextMsgId() { | ||
85 | - return msgIdSeq.incrementAndGet(); | ||
86 | - } | 67 | + |
87 | public boolean isDeviceTelemetryTopic(String topicName) { | 68 | public boolean isDeviceTelemetryTopic(String topicName) { |
88 | return telemetryTopicFilter.equals(topicName); | 69 | return telemetryTopicFilter.equals(topicName); |
89 | } | 70 | } |
@@ -96,11 +77,23 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext { | @@ -96,11 +77,23 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext { | ||
96 | } | 77 | } |
97 | 78 | ||
98 | public TcpTransportAdaptor getPayloadAdaptor() { | 79 | public TcpTransportAdaptor getPayloadAdaptor() { |
99 | - return adaptor; | 80 | + return this.adaptor; |
100 | } | 81 | } |
101 | 82 | ||
102 | 83 | ||
84 | + @Override | ||
85 | + public void setDeviceInfo(TransportDeviceInfo deviceInfo) { | ||
86 | + super.setDeviceInfo(deviceInfo); | ||
87 | + try { | ||
88 | + JsonNode additionalInfo = context.getMapper().readTree(deviceInfo.getAdditionalInfo()); | ||
89 | + if(additionalInfo !=null && additionalInfo.has(FastIotConstants.TCP_DEVICE_IDENTIFY_FILED)){ | ||
90 | + deviceCode = additionalInfo.get(FastIotConstants.TCP_DEVICE_IDENTIFY_FILED).asText(); | ||
91 | + } | ||
92 | + } catch (JsonProcessingException e) { | ||
93 | + log.trace("[{}][{}] Failed to fetch device additional info", sessionId, deviceInfo.getDeviceName(), e); | ||
94 | + } | ||
103 | 95 | ||
96 | + } | ||
104 | 97 | ||
105 | @Override | 98 | @Override |
106 | public void setDeviceProfile(DeviceProfile deviceProfile) { | 99 | public void setDeviceProfile(DeviceProfile deviceProfile) { |
@@ -138,50 +131,4 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext { | @@ -138,50 +131,4 @@ public class DeviceSessionCtx extends DeviceAwareSessionContext { | ||
138 | } | 131 | } |
139 | } | 132 | } |
140 | 133 | ||
141 | - | ||
142 | - | ||
143 | - | ||
144 | - | ||
145 | - | ||
146 | - public void addToQueue(TCPMessage msg) { | ||
147 | - msgQueueSize.incrementAndGet(); | ||
148 | - ReferenceCountUtil.retain(msg); | ||
149 | - msgQueue.add(msg); | ||
150 | - } | ||
151 | - | ||
152 | - public void tryProcessQueuedMsgs(Consumer<TCPMessage> msgProcessor) { | ||
153 | - while (!msgQueue.isEmpty()) { | ||
154 | - if (msgQueueProcessorLock.tryLock()) { | ||
155 | - try { | ||
156 | - TCPMessage msg; | ||
157 | - while ((msg = msgQueue.poll()) != null) { | ||
158 | - try { | ||
159 | - msgQueueSize.decrementAndGet(); | ||
160 | - msgProcessor.accept(msg); | ||
161 | - } finally { | ||
162 | - ReferenceCountUtil.safeRelease(msg); | ||
163 | - } | ||
164 | - } | ||
165 | - } finally { | ||
166 | - msgQueueProcessorLock.unlock(); | ||
167 | - } | ||
168 | - } else { | ||
169 | - return; | ||
170 | - } | ||
171 | - } | ||
172 | - } | ||
173 | - | ||
174 | - public int getMsgQueueSize() { | ||
175 | - return msgQueueSize.get(); | ||
176 | - } | ||
177 | - | ||
178 | - public void release() { | ||
179 | - if (!msgQueue.isEmpty()) { | ||
180 | - log.warn("doDisconnect for device {} but unprocessed messages {} left in the msg queue", getDeviceId(), msgQueue.size()); | ||
181 | - msgQueue.forEach(ReferenceCountUtil::safeRelease); | ||
182 | - msgQueue.clear(); | ||
183 | - } | ||
184 | - } | ||
185 | - | ||
186 | - | ||
187 | } | 134 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2022 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.transport.tcp.session; | ||
17 | + | ||
18 | +import io.netty.channel.ChannelFuture; | ||
19 | +import io.netty.handler.codec.mqtt.MqttMessage; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
22 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
23 | +import org.thingsboard.server.common.data.rpc.RpcStatus; | ||
24 | +import org.thingsboard.server.common.transport.SessionMsgListener; | ||
25 | +import org.thingsboard.server.common.transport.TransportService; | ||
26 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | ||
27 | +import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | ||
28 | +import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; | ||
29 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
30 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | ||
31 | +import org.thingsboard.server.transport.tcp.TcpTransportContext; | ||
32 | + | ||
33 | +import java.util.UUID; | ||
34 | + | ||
35 | +/** | ||
36 | + * Created by ashvayka on 19.01.17. | ||
37 | + */ | ||
38 | +@Slf4j | ||
39 | +public class TcpGatewayDeviceSessionCtx extends TcpDeviceWareSessionContext implements SessionMsgListener { | ||
40 | + | ||
41 | + private final TcpGatewaySessionHandler parent; | ||
42 | + private final TransportService transportService; | ||
43 | + | ||
44 | + public TcpGatewayDeviceSessionCtx(TcpTransportContext context, TcpGatewaySessionHandler parent, TransportDeviceInfo deviceInfo, | ||
45 | + DeviceProfile deviceProfile, TransportService transportService) { | ||
46 | + super(UUID.randomUUID(),context); | ||
47 | + this.parent = parent; | ||
48 | + setSessionInfo(SessionInfoProto.newBuilder() | ||
49 | + .setNodeId(parent.getNodeId()) | ||
50 | + .setSessionIdMSB(sessionId.getMostSignificantBits()) | ||
51 | + .setSessionIdLSB(sessionId.getLeastSignificantBits()) | ||
52 | + .setDeviceIdMSB(deviceInfo.getDeviceId().getId().getMostSignificantBits()) | ||
53 | + .setDeviceIdLSB(deviceInfo.getDeviceId().getId().getLeastSignificantBits()) | ||
54 | + .setTenantIdMSB(deviceInfo.getTenantId().getId().getMostSignificantBits()) | ||
55 | + .setTenantIdLSB(deviceInfo.getTenantId().getId().getLeastSignificantBits()) | ||
56 | + .setCustomerIdMSB(deviceInfo.getCustomerId().getId().getMostSignificantBits()) | ||
57 | + .setCustomerIdLSB(deviceInfo.getCustomerId().getId().getLeastSignificantBits()) | ||
58 | + .setDeviceName(deviceInfo.getDeviceName()) | ||
59 | + .setDeviceType(deviceInfo.getDeviceType()) | ||
60 | + .setGwSessionIdMSB(parent.getSessionId().getMostSignificantBits()) | ||
61 | + .setGwSessionIdLSB(parent.getSessionId().getLeastSignificantBits()) | ||
62 | + .setDeviceProfileIdMSB(deviceInfo.getDeviceProfileId().getId().getMostSignificantBits()) | ||
63 | + .setDeviceProfileIdLSB(deviceInfo.getDeviceProfileId().getId().getLeastSignificantBits()) | ||
64 | + .build()); | ||
65 | + setDeviceInfo(deviceInfo); | ||
66 | + setConnected(true); | ||
67 | + setDeviceProfile(deviceProfile); | ||
68 | + this.transportService = transportService; | ||
69 | + } | ||
70 | + | ||
71 | + @Override | ||
72 | + public UUID getSessionId() { | ||
73 | + return sessionId; | ||
74 | + } | ||
75 | + | ||
76 | + @Override | ||
77 | + public int nextMsgId() { | ||
78 | + return parent.nextMsgId(); | ||
79 | + } | ||
80 | + | ||
81 | + @Override | ||
82 | + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { | ||
83 | + try { | ||
84 | + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), response).ifPresent(parent::pushDeviceMsg); | ||
85 | + } catch (Exception e) { | ||
86 | + log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | ||
87 | + } | ||
88 | + } | ||
89 | + | ||
90 | + @Override | ||
91 | + public void onAttributeUpdate(UUID sessionId, TransportProtos.AttributeUpdateNotificationMsg notification) { | ||
92 | + log.trace("[{}] Received attributes update notification to device", sessionId); | ||
93 | + try { | ||
94 | + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::pushDeviceMsg); | ||
95 | + } catch (Exception e) { | ||
96 | + log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + @Override | ||
101 | + public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg request) { | ||
102 | + log.error("【{}】下发RPC命令【{}】给网关子设备", sessionId,request.getParams()); | ||
103 | + try { | ||
104 | + parent.getPayloadAdaptor().convertToPublish(this, request).ifPresent( | ||
105 | + payload -> { | ||
106 | + ChannelFuture channelFuture = parent.pushDeviceMsg(payload); | ||
107 | + if (request.getPersisted()) { | ||
108 | + channelFuture.addListener(result -> { | ||
109 | + if (result.cause() == null) { | ||
110 | + transportService.process(getSessionInfo(), request, RpcStatus.SENT, TransportServiceCallback.EMPTY); | ||
111 | + } | ||
112 | + }); | ||
113 | + } | ||
114 | + } | ||
115 | + ); | ||
116 | + } catch (Exception e) { | ||
117 | + transportService.process(getSessionInfo(), | ||
118 | + TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
119 | + .setRequestId(request.getRequestId()).setError("Failed to convert device RPC command to MQTT msg").build(), TransportServiceCallback.EMPTY); | ||
120 | + log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | ||
121 | + } | ||
122 | + } | ||
123 | + | ||
124 | + @Override | ||
125 | + public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { | ||
126 | + log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage()); | ||
127 | + parent.deregisterSession(getDeviceInfo().getDeviceName()); | ||
128 | + } | ||
129 | + | ||
130 | + @Override | ||
131 | + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { | ||
132 | + // This feature is not supported in the TB IoT Gateway yet. | ||
133 | + } | ||
134 | + | ||
135 | + @Override | ||
136 | + public void onDeviceDeleted(DeviceId deviceId) { | ||
137 | + parent.onDeviceDeleted(this.getSessionInfo().getDeviceName()); | ||
138 | + } | ||
139 | + | ||
140 | + private boolean isAckExpected(MqttMessage message) { | ||
141 | + return message.fixedHeader().qosLevel().value() > 0; | ||
142 | + } | ||
143 | + | ||
144 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2022 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.transport.tcp.session; | ||
17 | + | ||
18 | + | ||
19 | +import com.google.common.util.concurrent.FutureCallback; | ||
20 | +import com.google.common.util.concurrent.Futures; | ||
21 | +import com.google.common.util.concurrent.ListenableFuture; | ||
22 | +import com.google.common.util.concurrent.SettableFuture; | ||
23 | +import com.google.gson.*; | ||
24 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
25 | +import com.google.protobuf.ProtocolStringList; | ||
26 | +import io.netty.buffer.ByteBuf; | ||
27 | +import io.netty.buffer.Unpooled; | ||
28 | +import io.netty.channel.ChannelFuture; | ||
29 | +import io.netty.channel.ChannelHandlerContext; | ||
30 | +import io.netty.handler.codec.mqtt.MqttPublishMessage; | ||
31 | +import lombok.extern.slf4j.Slf4j; | ||
32 | +import org.springframework.util.CollectionUtils; | ||
33 | +import org.springframework.util.ConcurrentReferenceHashMap; | ||
34 | +import org.springframework.util.StringUtils; | ||
35 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
36 | +import org.thingsboard.server.common.data.yunteng.enums.TcpDataTypeEnum; | ||
37 | +import org.thingsboard.server.common.transport.TransportService; | ||
38 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | ||
39 | +import org.thingsboard.server.common.transport.adaptor.AdaptorException; | ||
40 | +import org.thingsboard.server.common.transport.adaptor.JsonConverter; | ||
41 | +import org.thingsboard.server.common.transport.adaptor.ProtoConverter; | ||
42 | +import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; | ||
43 | +import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | ||
44 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
45 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
46 | +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; | ||
47 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | ||
48 | +import org.thingsboard.server.transport.tcp.TcpTransportContext; | ||
49 | +import org.thingsboard.server.transport.tcp.adaptors.TcpTransportAdaptor; | ||
50 | +import org.thingsboard.server.transport.tcp.util.ByteUtils; | ||
51 | + | ||
52 | +import javax.annotation.Nullable; | ||
53 | +import java.io.UnsupportedEncodingException; | ||
54 | +import java.util.*; | ||
55 | +import java.util.concurrent.ConcurrentHashMap; | ||
56 | +import java.util.concurrent.ConcurrentMap; | ||
57 | +import java.util.concurrent.locks.Lock; | ||
58 | +import java.util.concurrent.locks.ReentrantLock; | ||
59 | + | ||
60 | +import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; | ||
61 | +import static org.thingsboard.server.common.transport.service.DefaultTransportService.*; | ||
62 | + | ||
63 | +/** | ||
64 | + * Created by ashvayka on 19.01.17. | ||
65 | + */ | ||
66 | +@Slf4j | ||
67 | +public class TcpGatewaySessionHandler { | ||
68 | + | ||
69 | + private static final String DEFAULT_DEVICE_TYPE = "default"; | ||
70 | + private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; | ||
71 | + private static final String DEVICE_PROPERTY = "device"; | ||
72 | + | ||
73 | + private final TcpTransportContext context; | ||
74 | + private final TransportService transportService; | ||
75 | + private final TransportDeviceInfo gateway; | ||
76 | + private final UUID sessionId; | ||
77 | + private final ConcurrentMap<String, Lock> deviceCreationLockMap; | ||
78 | + private final ConcurrentMap<String, TcpGatewayDeviceSessionCtx> devices; | ||
79 | + private final ConcurrentMap<String, ListenableFuture<TcpGatewayDeviceSessionCtx>> deviceFutures; | ||
80 | + private final ChannelHandlerContext channel; | ||
81 | + private final TcpDeviceSessionCtx deviceSessionCtx; | ||
82 | + | ||
83 | + public TcpGatewaySessionHandler(TcpDeviceSessionCtx deviceSessionCtx, UUID sessionId) { | ||
84 | + this.context = deviceSessionCtx.getContext(); | ||
85 | + this.transportService = context.getTransportService(); | ||
86 | + this.deviceSessionCtx = deviceSessionCtx; | ||
87 | + this.gateway = deviceSessionCtx.getDeviceInfo(); | ||
88 | + this.sessionId = sessionId; | ||
89 | + this.devices = new ConcurrentHashMap<>(); | ||
90 | + this.deviceFutures = new ConcurrentHashMap<>(); | ||
91 | + this.deviceCreationLockMap = createWeakMap(); | ||
92 | + this.channel = deviceSessionCtx.getChannel(); | ||
93 | + } | ||
94 | + | ||
95 | + ConcurrentReferenceHashMap<String, Lock> createWeakMap() { | ||
96 | + return new ConcurrentReferenceHashMap<>(16, ReferenceType.WEAK); | ||
97 | + } | ||
98 | + | ||
99 | + | ||
100 | + | ||
101 | + | ||
102 | + | ||
103 | + public void onDeviceTelemetry(TCPMessage tcpMessage) throws AdaptorException { | ||
104 | + Futures.addCallback(checkDeviceConnected(tcpMessage.getDeviceCode()), | ||
105 | + new FutureCallback<>() { | ||
106 | + @Override | ||
107 | + public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { | ||
108 | + String deviceName = deviceCtx.getDeviceInfo().getDeviceName(); | ||
109 | + try { | ||
110 | + TransportProtos.PostTelemetryMsg postTelemetryMsg = deviceCtx.getPayloadAdaptor().convertToPostTelemetry(deviceCtx,tcpMessage.getMessage()); | ||
111 | + processPostTelemetryMsg(deviceCtx, postTelemetryMsg, deviceName, tcpMessage.getRequestId()); | ||
112 | + } catch (Throwable e) { | ||
113 | + log.warn("[{}][{}] Failed to convert telemetry: {}", gateway.getDeviceId(), deviceName, tcpMessage.getMessage(), e); | ||
114 | + channel.close(); | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + @Override | ||
119 | + public void onFailure(Throwable t) { | ||
120 | + log.debug("[{}] Failed to process device telemetry command: {}", sessionId, tcpMessage.getDeviceCode(), t); | ||
121 | + } | ||
122 | + }, context.getExecutor()); | ||
123 | + } | ||
124 | + | ||
125 | + | ||
126 | + | ||
127 | + | ||
128 | + | ||
129 | + | ||
130 | + | ||
131 | + public void onGatewayDisconnect() { | ||
132 | + devices.forEach(this::deregisterSession); | ||
133 | + } | ||
134 | + | ||
135 | + public void onDeviceDeleted(String deviceName) { | ||
136 | + deregisterSession(deviceName); | ||
137 | + } | ||
138 | + | ||
139 | + public String getNodeId() { | ||
140 | + return context.getNodeId(); | ||
141 | + } | ||
142 | + | ||
143 | + public UUID getSessionId() { | ||
144 | + return sessionId; | ||
145 | + } | ||
146 | + | ||
147 | + public TcpTransportAdaptor getPayloadAdaptor() { | ||
148 | + return deviceSessionCtx.getPayloadAdaptor(); | ||
149 | + } | ||
150 | + | ||
151 | + void deregisterSession(String deviceName) { | ||
152 | + TcpGatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); | ||
153 | + if (deviceSessionCtx != null) { | ||
154 | + deregisterSession(deviceName, deviceSessionCtx); | ||
155 | + } else { | ||
156 | + log.debug("[{}] Device [{}] was already removed from the gateway session", sessionId, deviceName); | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + | ||
161 | + | ||
162 | + int nextMsgId() { | ||
163 | + return deviceSessionCtx.nextMsgId(); | ||
164 | + } | ||
165 | + | ||
166 | + private boolean isJsonPayloadType() { | ||
167 | + return true;//deviceSessionCtx.isJsonPayloadType(); | ||
168 | + } | ||
169 | + | ||
170 | + private void processOnConnect(MqttPublishMessage msg, String deviceName, String deviceType) { | ||
171 | + log.trace("[{}] onDeviceConnect: {}", sessionId, deviceName); | ||
172 | + Futures.addCallback(onDeviceConnect(deviceName, deviceType), new FutureCallback<TcpGatewayDeviceSessionCtx>() { | ||
173 | + @Override | ||
174 | + public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx result) { | ||
175 | + ack(msg); | ||
176 | + log.trace("[{}] onDeviceConnectOk: {}", sessionId, deviceName); | ||
177 | + } | ||
178 | + | ||
179 | + @Override | ||
180 | + public void onFailure(Throwable t) { | ||
181 | + log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, t); | ||
182 | + | ||
183 | + } | ||
184 | + }, context.getExecutor()); | ||
185 | + } | ||
186 | + | ||
187 | + private ListenableFuture<TcpGatewayDeviceSessionCtx> onDeviceConnect(String deviceCode, String deviceType) { | ||
188 | + TcpGatewayDeviceSessionCtx result = devices.get(deviceCode); | ||
189 | + if (result == null) { | ||
190 | + Lock deviceCreationLock = deviceCreationLockMap.computeIfAbsent(deviceCode, s -> new ReentrantLock()); | ||
191 | + deviceCreationLock.lock(); | ||
192 | + try { | ||
193 | + result = devices.get(deviceCode); | ||
194 | + if (result == null) { | ||
195 | + return getDeviceCreationFuture(deviceCode, deviceType); | ||
196 | + } else { | ||
197 | + return Futures.immediateFuture(result); | ||
198 | + } | ||
199 | + } finally { | ||
200 | + deviceCreationLock.unlock(); | ||
201 | + } | ||
202 | + } else { | ||
203 | + return Futures.immediateFuture(result); | ||
204 | + } | ||
205 | + } | ||
206 | + | ||
207 | + private ListenableFuture<TcpGatewayDeviceSessionCtx> getDeviceCreationFuture(String deviceName, String deviceType) { | ||
208 | + final SettableFuture<TcpGatewayDeviceSessionCtx> futureToSet = SettableFuture.create(); | ||
209 | + ListenableFuture<TcpGatewayDeviceSessionCtx> future = deviceFutures.putIfAbsent(deviceName, futureToSet); | ||
210 | + if (future != null) { | ||
211 | + return future; | ||
212 | + } | ||
213 | + try { | ||
214 | + transportService.process(GetOrCreateDeviceFromGatewayRequestMsg.newBuilder() | ||
215 | + .setDeviceName(deviceName) | ||
216 | + .setDeviceType(deviceType) | ||
217 | + .setGatewayIdMSB(gateway.getDeviceId().getId().getMostSignificantBits()) | ||
218 | + .setGatewayIdLSB(gateway.getDeviceId().getId().getLeastSignificantBits()).build(), | ||
219 | + new TransportServiceCallback<GetOrCreateDeviceFromGatewayResponse>() { | ||
220 | + @Override | ||
221 | + public void onSuccess(GetOrCreateDeviceFromGatewayResponse msg) { | ||
222 | + TcpGatewayDeviceSessionCtx deviceSessionCtx = new TcpGatewayDeviceSessionCtx(context,TcpGatewaySessionHandler.this, msg.getDeviceInfo(), msg.getDeviceProfile(), transportService); | ||
223 | + if (devices.putIfAbsent(deviceName, deviceSessionCtx) == null) { | ||
224 | + log.trace("[{}] First got or created device [{}], type [{}] for the gateway session", sessionId, deviceName, deviceType); | ||
225 | + SessionInfoProto deviceSessionInfo = deviceSessionCtx.getSessionInfo(); | ||
226 | + transportService.registerAsyncSession(deviceSessionInfo, deviceSessionCtx); | ||
227 | + transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder() | ||
228 | + .setSessionInfo(deviceSessionInfo) | ||
229 | + .setSessionEvent(SESSION_EVENT_MSG_OPEN) | ||
230 | + .setSubscribeToAttributes(SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG) | ||
231 | + .setSubscribeToRPC(SUBSCRIBE_TO_RPC_ASYNC_MSG) | ||
232 | + .build(), null); | ||
233 | + } | ||
234 | + futureToSet.set(devices.get(deviceName)); | ||
235 | + deviceFutures.remove(deviceName); | ||
236 | + } | ||
237 | + | ||
238 | + @Override | ||
239 | + public void onError(Throwable e) { | ||
240 | + log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, e); | ||
241 | + futureToSet.setException(e); | ||
242 | + deviceFutures.remove(deviceName); | ||
243 | + } | ||
244 | + }); | ||
245 | + return futureToSet; | ||
246 | + } catch (Throwable e) { | ||
247 | + deviceFutures.remove(deviceName); | ||
248 | + throw e; | ||
249 | + } | ||
250 | + } | ||
251 | + | ||
252 | + private int getMsgId(MqttPublishMessage mqttMsg) { | ||
253 | + return mqttMsg.variableHeader().packetId(); | ||
254 | + } | ||
255 | + | ||
256 | + public void onDeviceConnect(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
257 | + JsonElement json = getJson(mqttMsg); | ||
258 | + String deviceName = checkDeviceName(getDeviceName(json)); | ||
259 | + String deviceType = getDeviceType(json); | ||
260 | + processOnConnect(mqttMsg, deviceName, deviceType); | ||
261 | + } | ||
262 | + | ||
263 | + | ||
264 | + | ||
265 | + public void onDeviceDisconnect(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
266 | + String deviceName = checkDeviceName(getDeviceName(getJson(mqttMsg))); | ||
267 | + processOnDisconnect(mqttMsg, deviceName); | ||
268 | + } | ||
269 | + | ||
270 | + | ||
271 | + | ||
272 | + private void processOnDisconnect(MqttPublishMessage msg, String deviceName) { | ||
273 | + deregisterSession(deviceName); | ||
274 | + ack(msg); | ||
275 | + } | ||
276 | + | ||
277 | + | ||
278 | + | ||
279 | + | ||
280 | + private void processPostTelemetryMsg(TcpGatewayDeviceSessionCtx deviceCtx, TransportProtos.PostTelemetryMsg postTelemetryMsg, String deviceName, String msgId) { | ||
281 | + transportService.process(deviceCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(channel, deviceName, msgId, postTelemetryMsg)); | ||
282 | + } | ||
283 | + public void onDeviceClaim(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
284 | + int msgId = getMsgId(mqttMsg); | ||
285 | + ByteBuf payload = mqttMsg.payload(); | ||
286 | + JsonElement json = null;//JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
287 | + if (json.isJsonObject()) { | ||
288 | + JsonObject jsonObj = json.getAsJsonObject(); | ||
289 | + for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { | ||
290 | + String deviceName = deviceEntry.getKey(); | ||
291 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
292 | + new FutureCallback<TcpGatewayDeviceSessionCtx>() { | ||
293 | + @Override | ||
294 | + public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { | ||
295 | + if (!deviceEntry.getValue().isJsonObject()) { | ||
296 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
297 | + } | ||
298 | + try { | ||
299 | + DeviceId deviceId = deviceCtx.getDeviceId(); | ||
300 | + TransportProtos.ClaimDeviceMsg claimDeviceMsg = JsonConverter.convertToClaimDeviceProto(deviceId, deviceEntry.getValue()); | ||
301 | + processClaimDeviceMsg(deviceCtx, claimDeviceMsg, deviceName, msgId); | ||
302 | + } catch (Throwable e) { | ||
303 | + log.warn("[{}][{}] Failed to convert claim message: {}", gateway.getDeviceId(), deviceName, deviceEntry.getValue(), e); | ||
304 | + } | ||
305 | + } | ||
306 | + | ||
307 | + @Override | ||
308 | + public void onFailure(Throwable t) { | ||
309 | + log.debug("[{}] Failed to process device claiming command: {}", sessionId, deviceName, t); | ||
310 | + } | ||
311 | + }, context.getExecutor()); | ||
312 | + } | ||
313 | + } else { | ||
314 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
315 | + } | ||
316 | + } | ||
317 | + | ||
318 | + | ||
319 | + private void processClaimDeviceMsg(TcpGatewayDeviceSessionCtx deviceCtx, TransportProtos.ClaimDeviceMsg claimDeviceMsg, String deviceName, int msgId) { | ||
320 | + transportService.process(deviceCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(channel, deviceName, msgId+"", claimDeviceMsg)); | ||
321 | + } | ||
322 | + | ||
323 | + public void onDeviceAttributes(TCPMessage mqttMsg) throws AdaptorException { | ||
324 | + int msgId = 0;//getMsgId(mqttMsg); | ||
325 | + ByteBuf payload = null;//mqttMsg.payload(); | ||
326 | + JsonElement json = null;//JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
327 | + if (json.isJsonObject()) { | ||
328 | + JsonObject jsonObj = json.getAsJsonObject(); | ||
329 | + for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { | ||
330 | + String deviceName = deviceEntry.getKey(); | ||
331 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
332 | + new FutureCallback<TcpGatewayDeviceSessionCtx>() { | ||
333 | + @Override | ||
334 | + public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { | ||
335 | + if (!deviceEntry.getValue().isJsonObject()) { | ||
336 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
337 | + } | ||
338 | + TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject()); | ||
339 | + processPostAttributesMsg(deviceCtx, postAttributeMsg, deviceName, msgId); | ||
340 | + } | ||
341 | + | ||
342 | + @Override | ||
343 | + public void onFailure(Throwable t) { | ||
344 | + log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t); | ||
345 | + } | ||
346 | + }, context.getExecutor()); | ||
347 | + } | ||
348 | + } else { | ||
349 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
350 | + } | ||
351 | + } | ||
352 | + | ||
353 | + | ||
354 | + private void processPostAttributesMsg(TcpGatewayDeviceSessionCtx deviceCtx, TransportProtos.PostAttributeMsg postAttributeMsg, String deviceName, int msgId) { | ||
355 | + transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId+"", postAttributeMsg)); | ||
356 | + } | ||
357 | + | ||
358 | + public void onDeviceAttributesRequest(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
359 | + JsonElement json = null;//JsonMqttAdaptor.validateJsonPayload(sessionId, msg.payload()); | ||
360 | + if (json.isJsonObject()) { | ||
361 | + JsonObject jsonObj = json.getAsJsonObject(); | ||
362 | + int requestId = jsonObj.get("id").getAsInt(); | ||
363 | + String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); | ||
364 | + boolean clientScope = jsonObj.get("client").getAsBoolean(); | ||
365 | + Set<String> keys; | ||
366 | + if (jsonObj.has("key")) { | ||
367 | + keys = Collections.singleton(jsonObj.get("key").getAsString()); | ||
368 | + } else { | ||
369 | + JsonArray keysArray = jsonObj.get("keys").getAsJsonArray(); | ||
370 | + keys = new HashSet<>(); | ||
371 | + for (JsonElement keyObj : keysArray) { | ||
372 | + keys.add(keyObj.getAsString()); | ||
373 | + } | ||
374 | + } | ||
375 | + TransportProtos.GetAttributeRequestMsg requestMsg = toGetAttributeRequestMsg(requestId, clientScope, keys); | ||
376 | + processGetAttributeRequestMessage(mqttMsg, deviceName, requestMsg); | ||
377 | + } else { | ||
378 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
379 | + } | ||
380 | + } | ||
381 | + | ||
382 | + | ||
383 | + public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
384 | + int msgId = getMsgId(mqttMsg); | ||
385 | + ByteBuf payload = mqttMsg.payload(); | ||
386 | + JsonElement json = null;// JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
387 | + if (json.isJsonObject()) { | ||
388 | + JsonObject jsonObj = json.getAsJsonObject(); | ||
389 | + String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); | ||
390 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
391 | + new FutureCallback<TcpGatewayDeviceSessionCtx>() { | ||
392 | + @Override | ||
393 | + public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { | ||
394 | + Integer requestId = jsonObj.get("id").getAsInt(); | ||
395 | + String data = jsonObj.get("data").toString(); | ||
396 | + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
397 | + .setRequestId(requestId).setPayload(data).build(); | ||
398 | + processRpcResponseMsg(deviceCtx, rpcResponseMsg, deviceName, msgId); | ||
399 | + } | ||
400 | + | ||
401 | + @Override | ||
402 | + public void onFailure(Throwable t) { | ||
403 | + log.debug("[{}] Failed to process device Rpc response command: {}", sessionId, deviceName, t); | ||
404 | + } | ||
405 | + }, context.getExecutor()); | ||
406 | + } else { | ||
407 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
408 | + } | ||
409 | + } | ||
410 | + | ||
411 | + | ||
412 | + private void processRpcResponseMsg(TcpGatewayDeviceSessionCtx deviceCtx, TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg, String deviceName, int msgId) { | ||
413 | + transportService.process(deviceCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(channel, deviceName, msgId+"", rpcResponseMsg)); | ||
414 | + } | ||
415 | + | ||
416 | + private void processGetAttributeRequestMessage(MqttPublishMessage mqttMsg, String deviceName, TransportProtos.GetAttributeRequestMsg requestMsg) { | ||
417 | + int msgId = getMsgId(mqttMsg); | ||
418 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
419 | + new FutureCallback<TcpGatewayDeviceSessionCtx>() { | ||
420 | + @Override | ||
421 | + public void onSuccess(@Nullable TcpGatewayDeviceSessionCtx deviceCtx) { | ||
422 | + transportService.process(deviceCtx.getSessionInfo(), requestMsg, getPubAckCallback(channel, deviceName, msgId+"", requestMsg)); | ||
423 | + } | ||
424 | + | ||
425 | + @Override | ||
426 | + public void onFailure(Throwable t) { | ||
427 | + ack(mqttMsg); | ||
428 | + log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t); | ||
429 | + } | ||
430 | + }, context.getExecutor()); | ||
431 | + } | ||
432 | + | ||
433 | + private TransportProtos.GetAttributeRequestMsg toGetAttributeRequestMsg(int requestId, boolean clientScope, Set<String> keys) { | ||
434 | + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); | ||
435 | + result.setRequestId(requestId); | ||
436 | + | ||
437 | + if (clientScope) { | ||
438 | + result.addAllClientAttributeNames(keys); | ||
439 | + } else { | ||
440 | + result.addAllSharedAttributeNames(keys); | ||
441 | + } | ||
442 | + return result.build(); | ||
443 | + } | ||
444 | + | ||
445 | + private ListenableFuture<TcpGatewayDeviceSessionCtx> checkDeviceConnected(String deviceCode) { | ||
446 | + TcpGatewayDeviceSessionCtx ctx = devices.get(deviceCode); | ||
447 | + if (ctx == null) { | ||
448 | + log.debug("[{}] Missing device [{}] for the gateway session", sessionId, deviceCode); | ||
449 | + return onDeviceConnect(deviceCode, DEFAULT_DEVICE_TYPE); | ||
450 | + } else { | ||
451 | + return Futures.immediateFuture(ctx); | ||
452 | + } | ||
453 | + } | ||
454 | + | ||
455 | + private String checkDeviceName(String deviceName) { | ||
456 | + if (StringUtils.isEmpty(deviceName)) { | ||
457 | + throw new RuntimeException("Device name is empty!"); | ||
458 | + } else { | ||
459 | + return deviceName; | ||
460 | + } | ||
461 | + } | ||
462 | + | ||
463 | + private String getDeviceName(JsonElement json) { | ||
464 | + return json.getAsJsonObject().get(DEVICE_PROPERTY).getAsString(); | ||
465 | + } | ||
466 | + | ||
467 | + private String getDeviceType(JsonElement json) { | ||
468 | + JsonElement type = json.getAsJsonObject().get("type"); | ||
469 | + return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString(); | ||
470 | + } | ||
471 | + | ||
472 | + private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
473 | + return null;//JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | ||
474 | + } | ||
475 | + | ||
476 | + | ||
477 | + | ||
478 | + | ||
479 | + private void deregisterSession(String deviceName, TcpGatewayDeviceSessionCtx deviceSessionCtx) { | ||
480 | + transportService.deregisterSession(deviceSessionCtx.getSessionInfo()); | ||
481 | + transportService.process(deviceSessionCtx.getSessionInfo(), SESSION_EVENT_MSG_CLOSED, null); | ||
482 | + log.debug("[{}] Removed device [{}] from the gateway session", sessionId, deviceName); | ||
483 | + } | ||
484 | + | ||
485 | + private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final String deviceName, final String msgId, final T msg) { | ||
486 | + return new TransportServiceCallback<Void>() { | ||
487 | + @Override | ||
488 | + public void onSuccess(Void dummy) { | ||
489 | + log.trace("[{}][{}] Published msg: {}", sessionId, deviceName, msg); | ||
490 | + if(!StringUtils.isEmpty(msgId)){ | ||
491 | + pushDeviceMsg(new TCPMessage(msgId)); | ||
492 | + } | ||
493 | + } | ||
494 | + | ||
495 | + @Override | ||
496 | + public void onError(Throwable e) { | ||
497 | + log.trace("[{}] Failed to publish msg: {} for device: {}", sessionId, msg, deviceName, e); | ||
498 | + ctx.close(); | ||
499 | + } | ||
500 | + }; | ||
501 | + } | ||
502 | + /** | ||
503 | + * 往设备推送消息 | ||
504 | + * @param tcp | ||
505 | + * @return | ||
506 | + */ | ||
507 | + ChannelFuture pushDeviceMsg(TCPMessage tcp) { | ||
508 | + try { | ||
509 | + String message = tcp.getMessage(); | ||
510 | + byte[] payloadInBytes ; | ||
511 | + if(deviceSessionCtx.getPayloadType().equals(TcpDataTypeEnum.HEX)){ | ||
512 | + payloadInBytes = ByteUtils.hexStr2Bytes(message); | ||
513 | + }else{ | ||
514 | + payloadInBytes = message.getBytes(ByteUtils.UTF_8); | ||
515 | + } | ||
516 | +// ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); | ||
517 | +// ByteBuf payload = ALLOCATOR.buffer(); | ||
518 | +// payload.writeBytes(payloadInBytes); | ||
519 | + ByteBuf payload = Unpooled.copiedBuffer(payloadInBytes); | ||
520 | + | ||
521 | + return channel.writeAndFlush(payload); | ||
522 | + } catch (UnsupportedEncodingException e) { | ||
523 | + log.error(e.getMessage(),e); | ||
524 | + throw new RuntimeException(e); | ||
525 | + } | ||
526 | + } | ||
527 | + private void ack(MqttPublishMessage msg) { | ||
528 | + int msgId = getMsgId(msg); | ||
529 | + if (msgId > 0) { | ||
530 | + channel.writeAndFlush(msg); | ||
531 | + } | ||
532 | + } | ||
533 | +} | ||
534 | + |