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