mirror of
https://github.com/YunaiV/ruoyi-vue-pro.git
synced 2026-03-30 03:13:04 +00:00
feat:【iot】UDP 协议:兼容下行的时候,基于连接的 codec 处理
This commit is contained in:
@@ -223,11 +223,9 @@ public class IotGatewayConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler,
|
public IotUdpDownstreamSubscriber iotUdpDownstreamSubscriber(IotUdpUpstreamProtocol protocolHandler,
|
||||||
IotDeviceMessageService messageService,
|
IotDeviceMessageService messageService,
|
||||||
IotDeviceService deviceService,
|
|
||||||
IotUdpSessionManager sessionManager,
|
IotUdpSessionManager sessionManager,
|
||||||
IotMessageBus messageBus) {
|
IotMessageBus messageBus) {
|
||||||
return new IotUdpDownstreamSubscriber(protocolHandler, messageService, deviceService, sessionManager,
|
return new IotUdpDownstreamSubscriber(protocolHandler, messageService, sessionManager, messageBus);
|
||||||
messageBus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
|||||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
||||||
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;
|
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;
|
||||||
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler;
|
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.router.IotUdpDownstreamHandler;
|
||||||
import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
|
|
||||||
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
|
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -25,8 +24,6 @@ public class IotUdpDownstreamSubscriber implements IotMessageSubscriber<IotDevic
|
|||||||
|
|
||||||
private final IotDeviceMessageService messageService;
|
private final IotDeviceMessageService messageService;
|
||||||
|
|
||||||
private final IotDeviceService deviceService;
|
|
||||||
|
|
||||||
private final IotUdpSessionManager sessionManager;
|
private final IotUdpSessionManager sessionManager;
|
||||||
|
|
||||||
private final IotMessageBus messageBus;
|
private final IotMessageBus messageBus;
|
||||||
@@ -36,7 +33,7 @@ public class IotUdpDownstreamSubscriber implements IotMessageSubscriber<IotDevic
|
|||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// 初始化下游处理器
|
// 初始化下游处理器
|
||||||
this.downstreamHandler = new IotUdpDownstreamHandler(messageService, deviceService, sessionManager, protocol);
|
this.downstreamHandler = new IotUdpDownstreamHandler(messageService, sessionManager, protocol);
|
||||||
|
|
||||||
messageBus.register(this);
|
messageBus.register(this);
|
||||||
log.info("[init][UDP 下游订阅者初始化完成,服务器 ID: {},Topic: {}]",
|
log.info("[init][UDP 下游订阅者初始化完成,服务器 ID: {},Topic: {}]",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager;
|
|||||||
|
|
||||||
import io.vertx.core.buffer.Buffer;
|
import io.vertx.core.buffer.Buffer;
|
||||||
import io.vertx.core.datagram.DatagramSocket;
|
import io.vertx.core.datagram.DatagramSocket;
|
||||||
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@@ -29,9 +30,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
public class IotUdpSessionManager {
|
public class IotUdpSessionManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备 ID -> 设备地址(用于下行消息发送)
|
* 设备 ID -> 会话信息(包含地址和 codecType)
|
||||||
*/
|
*/
|
||||||
private final Map<Long, InetSocketAddress> deviceAddressMap = new ConcurrentHashMap<>();
|
private final Map<Long, SessionInfo> deviceSessionMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备地址 Key -> 最后活跃时间(用于清理)
|
* 设备地址 Key -> 最后活跃时间(用于清理)
|
||||||
@@ -44,18 +45,41 @@ public class IotUdpSessionManager {
|
|||||||
private final Map<String, Long> addressDeviceMap = new ConcurrentHashMap<>();
|
private final Map<String, Long> addressDeviceMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新设备地址(每次收到上行消息时调用)
|
* 更新设备会话(每次收到上行消息时调用)
|
||||||
|
*
|
||||||
|
* @param deviceId 设备 ID
|
||||||
|
* @param address 设备地址
|
||||||
|
* @param codecType 消息编解码类型
|
||||||
|
*/
|
||||||
|
public void updateDeviceSession(Long deviceId, InetSocketAddress address, String codecType) {
|
||||||
|
String addressKey = buildAddressKey(address);
|
||||||
|
// 更新设备会话映射
|
||||||
|
deviceSessionMap.put(deviceId, new SessionInfo().setAddress(address).setCodecType(codecType));
|
||||||
|
lastActiveTimeMap.put(addressKey, LocalDateTime.now());
|
||||||
|
addressDeviceMap.put(addressKey, deviceId);
|
||||||
|
log.debug("[updateDeviceSession][更新设备会话,设备 ID: {},地址: {},codecType: {}]", deviceId, addressKey, codecType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新设备地址(兼容旧接口,默认不更新 codecType)
|
||||||
*
|
*
|
||||||
* @param deviceId 设备 ID
|
* @param deviceId 设备 ID
|
||||||
* @param address 设备地址
|
* @param address 设备地址
|
||||||
*/
|
*/
|
||||||
public void updateDeviceAddress(Long deviceId, InetSocketAddress address) {
|
public void updateDeviceAddress(Long deviceId, InetSocketAddress address) {
|
||||||
String addressKey = buildAddressKey(address);
|
SessionInfo sessionInfo = deviceSessionMap.get(deviceId);
|
||||||
// 更新设备地址映射
|
String codecType = sessionInfo != null ? sessionInfo.getCodecType() : null;
|
||||||
deviceAddressMap.put(deviceId, address);
|
updateDeviceSession(deviceId, address, codecType);
|
||||||
lastActiveTimeMap.put(addressKey, LocalDateTime.now());
|
}
|
||||||
addressDeviceMap.put(addressKey, deviceId);
|
|
||||||
log.debug("[updateDeviceAddress][更新设备地址,设备 ID: {},地址: {}]", deviceId, addressKey);
|
/**
|
||||||
|
* 获取设备会话信息
|
||||||
|
*
|
||||||
|
* @param deviceId 设备 ID
|
||||||
|
* @return 会话信息
|
||||||
|
*/
|
||||||
|
public SessionInfo getSessionInfo(Long deviceId) {
|
||||||
|
return deviceSessionMap.get(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,7 +89,7 @@ public class IotUdpSessionManager {
|
|||||||
* @return 是否在线
|
* @return 是否在线
|
||||||
*/
|
*/
|
||||||
public boolean isDeviceOnline(Long deviceId) {
|
public boolean isDeviceOnline(Long deviceId) {
|
||||||
return deviceAddressMap.containsKey(deviceId);
|
return deviceSessionMap.containsKey(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,12 +111,13 @@ public class IotUdpSessionManager {
|
|||||||
* @return 是否发送成功
|
* @return 是否发送成功
|
||||||
*/
|
*/
|
||||||
public boolean sendToDevice(Long deviceId, byte[] data, DatagramSocket socket) {
|
public boolean sendToDevice(Long deviceId, byte[] data, DatagramSocket socket) {
|
||||||
InetSocketAddress address = deviceAddressMap.get(deviceId);
|
SessionInfo sessionInfo = deviceSessionMap.get(deviceId);
|
||||||
if (address == null) {
|
if (sessionInfo == null || sessionInfo.getAddress() == null) {
|
||||||
log.warn("[sendToDevice][设备地址不存在,设备 ID: {}]", deviceId);
|
log.warn("[sendToDevice][设备会话不存在,设备 ID: {}]", deviceId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InetSocketAddress address = sessionInfo.getAddress();
|
||||||
try {
|
try {
|
||||||
socket.send(Buffer.buffer(data), address.getPort(), address.getHostString(), result -> {
|
socket.send(Buffer.buffer(data), address.getPort(), address.getHostString(), result -> {
|
||||||
if (result.succeeded()) {
|
if (result.succeeded()) {
|
||||||
@@ -134,8 +159,8 @@ public class IotUdpSessionManager {
|
|||||||
iterator.remove();
|
iterator.remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
InetSocketAddress address = deviceAddressMap.remove(deviceId);
|
SessionInfo sessionInfo = deviceSessionMap.remove(deviceId);
|
||||||
if (address == null) {
|
if (sessionInfo == null) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -157,4 +182,22 @@ public class IotUdpSessionManager {
|
|||||||
return address.getHostString() + ":" + address.getPort();
|
return address.getHostString() + ":" + address.getPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会话信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class SessionInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备地址
|
||||||
|
*/
|
||||||
|
private InetSocketAddress address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息编解码类型
|
||||||
|
*/
|
||||||
|
private String codecType;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package cn.iocoder.yudao.module.iot.gateway.protocol.udp.router;
|
package cn.iocoder.yudao.module.iot.gateway.protocol.udp.router;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
|
|
||||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol;
|
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotUdpUpstreamProtocol;
|
||||||
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;
|
import cn.iocoder.yudao.module.iot.gateway.protocol.udp.manager.IotUdpSessionManager;
|
||||||
import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
|
|
||||||
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
|
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
|
||||||
import io.vertx.core.datagram.DatagramSocket;
|
import io.vertx.core.datagram.DatagramSocket;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -19,18 +17,14 @@ public class IotUdpDownstreamHandler {
|
|||||||
|
|
||||||
private final IotDeviceMessageService deviceMessageService;
|
private final IotDeviceMessageService deviceMessageService;
|
||||||
|
|
||||||
private final IotDeviceService deviceService;
|
|
||||||
|
|
||||||
private final IotUdpSessionManager sessionManager;
|
private final IotUdpSessionManager sessionManager;
|
||||||
|
|
||||||
private final IotUdpUpstreamProtocol protocol;
|
private final IotUdpUpstreamProtocol protocol;
|
||||||
|
|
||||||
public IotUdpDownstreamHandler(IotDeviceMessageService deviceMessageService,
|
public IotUdpDownstreamHandler(IotDeviceMessageService deviceMessageService,
|
||||||
IotDeviceService deviceService,
|
|
||||||
IotUdpSessionManager sessionManager,
|
IotUdpSessionManager sessionManager,
|
||||||
IotUdpUpstreamProtocol protocol) {
|
IotUdpUpstreamProtocol protocol) {
|
||||||
this.deviceMessageService = deviceMessageService;
|
this.deviceMessageService = deviceMessageService;
|
||||||
this.deviceService = deviceService;
|
|
||||||
this.sessionManager = sessionManager;
|
this.sessionManager = sessionManager;
|
||||||
this.protocol = protocol;
|
this.protocol = protocol;
|
||||||
}
|
}
|
||||||
@@ -45,21 +39,15 @@ public class IotUdpDownstreamHandler {
|
|||||||
log.info("[handle][处理下行消息,设备 ID: {},方法: {},消息 ID: {}]",
|
log.info("[handle][处理下行消息,设备 ID: {},方法: {},消息 ID: {}]",
|
||||||
message.getDeviceId(), message.getMethod(), message.getId());
|
message.getDeviceId(), message.getMethod(), message.getId());
|
||||||
|
|
||||||
// 1.1 获取设备信息
|
// 1. 获取会话信息(包含 codecType)
|
||||||
IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(message.getDeviceId());
|
IotUdpSessionManager.SessionInfo sessionInfo = sessionManager.getSessionInfo(message.getDeviceId());
|
||||||
if (deviceInfo == null) {
|
if (sessionInfo == null) {
|
||||||
log.error("[handle][设备不存在,设备 ID: {}]", message.getDeviceId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 1.2 检查设备是否在线(即是否有地址映射)
|
|
||||||
if (sessionManager.isDeviceOffline(message.getDeviceId())) {
|
|
||||||
log.warn("[handle][设备不在线,设备 ID: {}]", message.getDeviceId());
|
log.warn("[handle][设备不在线,设备 ID: {}]", message.getDeviceId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 根据产品 Key 和设备名称编码消息,并发送到设备
|
// 2. 使用会话中的 codecType 编码消息,并发送到设备
|
||||||
byte[] bytes = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(),
|
byte[] bytes = deviceMessageService.encodeDeviceMessage(message, sessionInfo.getCodecType());
|
||||||
deviceInfo.getDeviceName());
|
|
||||||
DatagramSocket socket = protocol.getUdpSocket();
|
DatagramSocket socket = protocol.getUdpSocket();
|
||||||
if (socket == null) {
|
if (socket == null) {
|
||||||
log.error("[handle][UDP Socket 不可用,设备 ID: {}]", message.getDeviceId());
|
log.error("[handle][UDP Socket 不可用,设备 ID: {}]", message.getDeviceId());
|
||||||
|
|||||||
@@ -195,8 +195,8 @@ public class IotUdpUpstreamHandler {
|
|||||||
// 3.1 生成 JWT Token(无状态)
|
// 3.1 生成 JWT Token(无状态)
|
||||||
String token = deviceTokenService.createToken(device.getProductKey(), device.getDeviceName());
|
String token = deviceTokenService.createToken(device.getProductKey(), device.getDeviceName());
|
||||||
|
|
||||||
// 3.2 更新设备地址映射(用于下行消息)
|
// 3.2 更新设备会话信息(用于下行消息,保存 codecType)
|
||||||
sessionManager.updateDeviceAddress(device.getId(), senderAddress);
|
sessionManager.updateDeviceSession(device.getId(), senderAddress, codecType);
|
||||||
|
|
||||||
// 3.3 发送上线消息
|
// 3.3 发送上线消息
|
||||||
sendOnlineMessage(device);
|
sendOnlineMessage(device);
|
||||||
@@ -298,8 +298,8 @@ public class IotUdpUpstreamHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 更新设备地址映射(保持最新)
|
// 3. 更新设备会话信息(保持最新,保存 codecType)
|
||||||
sessionManager.updateDeviceAddress(device.getId(), senderAddress);
|
sessionManager.updateDeviceSession(device.getId(), senderAddress, codecType);
|
||||||
|
|
||||||
// 4. 将 body 设置为实际的 params,发送消息到消息总线
|
// 4. 将 body 设置为实际的 params,发送消息到消息总线
|
||||||
message.setParams(body);
|
message.setParams(body);
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
|
package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.HexUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
||||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
import java.net.DatagramSocket;
|
import java.net.DatagramSocket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -24,10 +26,16 @@ import java.util.Map;
|
|||||||
*
|
*
|
||||||
* <p>测试场景:直连设备(IotProductDeviceTypeEnum 的 DIRECT 类型)通过 UDP 协议直接连接平台
|
* <p>测试场景:直连设备(IotProductDeviceTypeEnum 的 DIRECT 类型)通过 UDP 协议直接连接平台
|
||||||
*
|
*
|
||||||
|
* <p>支持两种编解码格式:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link IotTcpJsonDeviceMessageCodec} - JSON 格式</li>
|
||||||
|
* <li>{@link IotTcpBinaryDeviceMessageCodec} - 二进制格式</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* <p>使用步骤:
|
* <p>使用步骤:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>启动 yudao-module-iot-gateway 服务(UDP 端口 8092)</li>
|
* <li>启动 yudao-module-iot-gateway 服务(UDP 端口 8093)</li>
|
||||||
* <li>运行 {@link #testDeviceRegister()} 测试直连设备动态注册(一型一密)</li>
|
* <li>修改 {@link #CODEC} 选择测试的编解码格式</li>
|
||||||
* <li>运行 {@link #testAuth()} 获取设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li>
|
* <li>运行 {@link #testAuth()} 获取设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li>
|
||||||
* <li>运行以下测试方法:
|
* <li>运行以下测试方法:
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -48,6 +56,10 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
|
|||||||
private static final int SERVER_PORT = 8093;
|
private static final int SERVER_PORT = 8093;
|
||||||
private static final int TIMEOUT_MS = 5000;
|
private static final int TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
|
// ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
|
||||||
|
private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
|
||||||
|
// private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
|
||||||
|
|
||||||
// ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
|
// ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
|
||||||
private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
|
private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
|
||||||
private static final String DEVICE_NAME = "small";
|
private static final String DEVICE_NAME = "small";
|
||||||
@@ -65,27 +77,32 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAuth() throws Exception {
|
public void testAuth() throws Exception {
|
||||||
// 1.1 构建请求
|
// 1.1 构建认证消息
|
||||||
IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);
|
IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);
|
||||||
IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
|
IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
|
||||||
.setClientId(authInfo.getClientId())
|
.setClientId(authInfo.getClientId())
|
||||||
.setUsername(authInfo.getUsername())
|
.setUsername(authInfo.getUsername())
|
||||||
.setPassword(authInfo.getPassword());
|
.setPassword(authInfo.getPassword());
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), "auth", authReqDTO, null, null, null);
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
// 1.2 编码
|
||||||
.put("method", "auth")
|
byte[] payload = CODEC.encode(request);
|
||||||
.put("params", authReqDTO)
|
log.info("[testAuth][Codec: {}, 请求消息: {}, 数据包长度: {} 字节]", CODEC.type(), request, payload.length);
|
||||||
.build());
|
if (CODEC instanceof IotTcpBinaryDeviceMessageCodec) {
|
||||||
// 1.2 输出请求
|
log.info("[testAuth][二进制数据包(HEX): {}]", HexUtil.encodeHexStr(payload));
|
||||||
log.info("[testAuth][请求体: {}]", payload);
|
}
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testAuth][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
log.info("[testAuth][请将返回的 token 复制到 TOKEN 常量中]");
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testAuth][响应消息: {}]", response);
|
||||||
|
log.info("[testAuth][请将返回的 token 复制到 TOKEN 常量中]");
|
||||||
|
} else {
|
||||||
|
log.warn("[testAuth][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,25 +113,30 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testPropertyPost() throws Exception {
|
public void testPropertyPost() throws Exception {
|
||||||
// 1.1 构建请求(UDP 协议:token 放在 params 中)
|
// 1.1 构建属性上报消息(UDP 协议:token 放在 params 中)
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())
|
IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()
|
||||||
.put("params", withToken(IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()
|
|
||||||
.put("width", 1)
|
.put("width", 1)
|
||||||
.put("height", "2")
|
.put("height", "2")
|
||||||
.build())))
|
.build())),
|
||||||
.build());
|
null, null, null);
|
||||||
// 1.2 输出请求
|
// 1.2 编码
|
||||||
log.info("[testPropertyPost][请求体: {}]", payload);
|
byte[] payload = CODEC.encode(request);
|
||||||
|
log.info("[testPropertyPost][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2. 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testPropertyPost][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testPropertyPost][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testPropertyPost][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,60 +147,30 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEventPost() throws Exception {
|
public void testEventPost() throws Exception {
|
||||||
// 1.1 构建请求(UDP 协议:token 放在 params 中)
|
// 1.1 构建事件上报消息(UDP 协议:token 放在 params 中)
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.EVENT_POST.getMethod())
|
IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(IotDeviceEventPostReqDTO.of(
|
||||||
.put("params", withToken(IotDeviceEventPostReqDTO.of(
|
|
||||||
"eat",
|
"eat",
|
||||||
MapUtil.<String, Object>builder().put("rice", 3).build(),
|
MapUtil.<String, Object>builder().put("rice", 3).build(),
|
||||||
System.currentTimeMillis())))
|
System.currentTimeMillis())),
|
||||||
.build());
|
null, null, null);
|
||||||
// 1.2 输出请求
|
// 1.2 编码
|
||||||
log.info("[testEventPost][请求体: {}]", payload);
|
byte[] payload = CODEC.encode(request);
|
||||||
|
log.info("[testEventPost][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testEventPost][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
}
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
}
|
log.info("[testEventPost][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
// ===================== 动态注册测试 =====================
|
log.warn("[testEventPost][未收到响应]");
|
||||||
|
}
|
||||||
/**
|
|
||||||
* 直连设备动态注册测试(一型一密)
|
|
||||||
* <p>
|
|
||||||
* 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret)
|
|
||||||
* <p>
|
|
||||||
* 注意:此接口不需要 Token 认证
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testDeviceRegister() throws Exception {
|
|
||||||
// 1.1 构建请求参数
|
|
||||||
IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO();
|
|
||||||
reqDTO.setProductKey(PRODUCT_KEY);
|
|
||||||
reqDTO.setDeviceName("test-" + System.currentTimeMillis());
|
|
||||||
reqDTO.setProductSecret("test-product-secret");
|
|
||||||
// 1.2 构建请求
|
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
|
||||||
.put("method", IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod())
|
|
||||||
.put("params", reqDTO)
|
|
||||||
.build());
|
|
||||||
// 1.3 输出请求
|
|
||||||
log.info("[testDeviceRegister][请求体: {}]", payload);
|
|
||||||
|
|
||||||
// 2.1 发送请求
|
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
|
||||||
String response = sendAndReceive(socket, payload);
|
|
||||||
// 2.2 输出结果
|
|
||||||
log.info("[testDeviceRegister][响应体: {}]", response);
|
|
||||||
log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,20 +193,18 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送 UDP 请求并接收响应
|
* 发送 UDP 请求并接收响应
|
||||||
*
|
*
|
||||||
* @param socket UDP Socket
|
* @param socket UDP Socket
|
||||||
* @param payload 请求体
|
* @param payload 请求数据
|
||||||
* @return 响应内容
|
* @return 响应数据
|
||||||
*/
|
*/
|
||||||
public static String sendAndReceive(DatagramSocket socket, String payload) throws Exception {
|
public static byte[] sendAndReceive(DatagramSocket socket, byte[] payload) throws Exception {
|
||||||
byte[] sendData = payload.getBytes(StandardCharsets.UTF_8);
|
|
||||||
InetAddress address = InetAddress.getByName(SERVER_HOST);
|
InetAddress address = InetAddress.getByName(SERVER_HOST);
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, SERVER_PORT);
|
DatagramPacket sendPacket = new DatagramPacket(payload, payload.length, address, SERVER_PORT);
|
||||||
socket.send(sendPacket);
|
socket.send(sendPacket);
|
||||||
|
|
||||||
// 接收响应
|
// 接收响应
|
||||||
@@ -222,7 +212,9 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
|
|||||||
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
||||||
try {
|
try {
|
||||||
socket.receive(receivePacket);
|
socket.receive(receivePacket);
|
||||||
return new String(receivePacket.getData(), 0, receivePacket.getLength(), StandardCharsets.UTF_8);
|
byte[] response = new byte[receivePacket.getLength()];
|
||||||
|
System.arraycopy(receivePacket.getData(), 0, response, 0, receivePacket.getLength());
|
||||||
|
return response;
|
||||||
} catch (java.net.SocketTimeoutException e) {
|
} catch (java.net.SocketTimeoutException e) {
|
||||||
log.warn("[sendAndReceive][接收响应超时]");
|
log.warn("[sendAndReceive][接收响应超时]");
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
|
package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.HexUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
||||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
|
import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPackPostReqDTO;
|
||||||
@@ -12,6 +13,9 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;
|
|||||||
import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -28,9 +32,16 @@ import static cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotDirectDeviceUd
|
|||||||
*
|
*
|
||||||
* <p>测试场景:网关设备(IotProductDeviceTypeEnum 的 GATEWAY 类型)通过 UDP 协议管理子设备拓扑关系
|
* <p>测试场景:网关设备(IotProductDeviceTypeEnum 的 GATEWAY 类型)通过 UDP 协议管理子设备拓扑关系
|
||||||
*
|
*
|
||||||
|
* <p>支持两种编解码格式:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link IotTcpJsonDeviceMessageCodec} - JSON 格式</li>
|
||||||
|
* <li>{@link IotTcpBinaryDeviceMessageCodec} - 二进制格式</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* <p>使用步骤:
|
* <p>使用步骤:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>启动 yudao-module-iot-gateway 服务(UDP 端口 8093)</li>
|
* <li>启动 yudao-module-iot-gateway 服务(UDP 端口 8093)</li>
|
||||||
|
* <li>修改 {@link #CODEC} 选择测试的编解码格式</li>
|
||||||
* <li>运行 {@link #testAuth()} 获取网关设备 token,将返回的 token 粘贴到 {@link #GATEWAY_TOKEN} 常量</li>
|
* <li>运行 {@link #testAuth()} 获取网关设备 token,将返回的 token 粘贴到 {@link #GATEWAY_TOKEN} 常量</li>
|
||||||
* <li>运行以下测试方法:
|
* <li>运行以下测试方法:
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -50,10 +61,12 @@ import static cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotDirectDeviceUd
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
||||||
|
|
||||||
private static final String SERVER_HOST = "127.0.0.1";
|
|
||||||
private static final int SERVER_PORT = 8093;
|
|
||||||
private static final int TIMEOUT_MS = 5000;
|
private static final int TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
|
// ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
|
||||||
|
private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
|
||||||
|
// private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
|
||||||
|
|
||||||
// ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
|
// ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
|
||||||
private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
|
private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
|
||||||
private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
|
private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
|
||||||
@@ -76,28 +89,33 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAuth() throws Exception {
|
public void testAuth() throws Exception {
|
||||||
// 1.1 构建请求
|
// 1.1 构建认证消息
|
||||||
IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(
|
IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(
|
||||||
GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);
|
GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);
|
||||||
IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
|
IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
|
||||||
.setClientId(authInfo.getClientId())
|
.setClientId(authInfo.getClientId())
|
||||||
.setUsername(authInfo.getUsername())
|
.setUsername(authInfo.getUsername())
|
||||||
.setPassword(authInfo.getPassword());
|
.setPassword(authInfo.getPassword());
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), "auth", authReqDTO, null, null, null);
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
// 1.2 编码
|
||||||
.put("method", "auth")
|
byte[] payload = CODEC.encode(request);
|
||||||
.put("params", authReqDTO)
|
log.info("[testAuth][Codec: {}, 请求消息: {}, 数据包长度: {} 字节]", CODEC.type(), request, payload.length);
|
||||||
.build());
|
if (CODEC instanceof IotTcpBinaryDeviceMessageCodec) {
|
||||||
// 1.2 输出请求
|
log.info("[testAuth][二进制数据包(HEX): {}]", HexUtil.encodeHexStr(payload));
|
||||||
log.info("[testAuth][请求体: {}]", payload);
|
}
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testAuth][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
log.info("[testAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]");
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testAuth][响应消息: {}]", response);
|
||||||
|
log.info("[testAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]");
|
||||||
|
} else {
|
||||||
|
log.warn("[testAuth][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,21 +138,26 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
|||||||
// 1.2 构建请求参数
|
// 1.2 构建请求参数
|
||||||
IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();
|
IotDeviceTopoAddReqDTO params = new IotDeviceTopoAddReqDTO();
|
||||||
params.setSubDevices(Collections.singletonList(subDeviceAuth));
|
params.setSubDevices(Collections.singletonList(subDeviceAuth));
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.TOPO_ADD.getMethod())
|
IotDeviceMessageMethodEnum.TOPO_ADD.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(params),
|
||||||
.put("params", withToken(params))
|
null, null, null);
|
||||||
.build());
|
// 1.3 编码
|
||||||
// 1.3 输出请求
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testTopoAdd][请求体: {}]", payload);
|
log.info("[testTopoAdd][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testTopoAdd][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testTopoAdd][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testTopoAdd][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,21 +172,26 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
|||||||
IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();
|
IotDeviceTopoDeleteReqDTO params = new IotDeviceTopoDeleteReqDTO();
|
||||||
params.setSubDevices(Collections.singletonList(
|
params.setSubDevices(Collections.singletonList(
|
||||||
new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));
|
new IotDeviceIdentity(SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME)));
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod())
|
IotDeviceMessageMethodEnum.TOPO_DELETE.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(params),
|
||||||
.put("params", withToken(params))
|
null, null, null);
|
||||||
.build());
|
// 1.2 编码
|
||||||
// 1.2 输出请求
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testTopoDelete][请求体: {}]", payload);
|
log.info("[testTopoDelete][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testTopoDelete][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testTopoDelete][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testTopoDelete][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,21 +204,26 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
|||||||
public void testTopoGet() throws Exception {
|
public void testTopoGet() throws Exception {
|
||||||
// 1.1 构建请求参数(目前为空,预留扩展)
|
// 1.1 构建请求参数(目前为空,预留扩展)
|
||||||
IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();
|
IotDeviceTopoGetReqDTO params = new IotDeviceTopoGetReqDTO();
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.TOPO_GET.getMethod())
|
IotDeviceMessageMethodEnum.TOPO_GET.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(params),
|
||||||
.put("params", withToken(params))
|
null, null, null);
|
||||||
.build());
|
// 1.2 编码
|
||||||
// 1.2 输出请求
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testTopoGet][请求体: {}]", payload);
|
log.info("[testTopoGet][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testTopoGet][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testTopoGet][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testTopoGet][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,21 +242,26 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
|||||||
IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();
|
IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();
|
||||||
subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);
|
subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);
|
||||||
subDevice.setDeviceName("mougezishebei");
|
subDevice.setDeviceName("mougezishebei");
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod())
|
IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(Collections.singletonList(subDevice)),
|
||||||
.put("params", withToken(Collections.singletonList(subDevice)))
|
null, null, null);
|
||||||
.build());
|
// 1.2 编码
|
||||||
// 1.2 输出请求
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testSubDeviceRegister][请求体: {}]", payload);
|
log.info("[testSubDeviceRegister][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testSubDeviceRegister][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testSubDeviceRegister][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testSubDeviceRegister][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,21 +306,26 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
|
|||||||
params.setProperties(gatewayProperties);
|
params.setProperties(gatewayProperties);
|
||||||
params.setEvents(gatewayEvents);
|
params.setEvents(gatewayEvents);
|
||||||
params.setSubDevices(List.of(subDeviceData));
|
params.setSubDevices(List.of(subDeviceData));
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod())
|
IotDeviceMessageMethodEnum.PROPERTY_PACK_POST.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(params),
|
||||||
.put("params", withToken(params))
|
null, null, null);
|
||||||
.build());
|
// 1.7 编码
|
||||||
// 1.7 输出请求
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testPropertyPackPost][请求体: {}]", payload);
|
log.info("[testPropertyPackPost][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testPropertyPackPost][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testPropertyPackPost][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testPropertyPackPost][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
|
package cn.iocoder.yudao.module.iot.gateway.protocol.udp;
|
||||||
|
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
|
import cn.hutool.core.util.HexUtil;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
|
||||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||||
|
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
|
import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
|
||||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec;
|
||||||
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@@ -25,10 +29,17 @@ import static cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotDirectDeviceUd
|
|||||||
* <p><b>重要说明:子设备无法直接连接平台,所有请求均由网关设备(Gateway)代为转发。</b>
|
* <p><b>重要说明:子设备无法直接连接平台,所有请求均由网关设备(Gateway)代为转发。</b>
|
||||||
* <p>网关设备转发子设备请求时,Token 使用子设备自己的信息。
|
* <p>网关设备转发子设备请求时,Token 使用子设备自己的信息。
|
||||||
*
|
*
|
||||||
|
* <p>支持两种编解码格式:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link IotTcpJsonDeviceMessageCodec} - JSON 格式</li>
|
||||||
|
* <li>{@link IotTcpBinaryDeviceMessageCodec} - 二进制格式</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
* <p>使用步骤:
|
* <p>使用步骤:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>启动 yudao-module-iot-gateway 服务(UDP 端口 8093)</li>
|
* <li>启动 yudao-module-iot-gateway 服务(UDP 端口 8093)</li>
|
||||||
* <li>确保子设备已通过 {@link IotGatewayDeviceUdpProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>
|
* <li>确保子设备已通过 {@link IotGatewayDeviceUdpProtocolIntegrationTest#testTopoAdd()} 绑定到网关</li>
|
||||||
|
* <li>修改 {@link #CODEC} 选择测试的编解码格式</li>
|
||||||
* <li>运行 {@link #testAuth()} 获取子设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li>
|
* <li>运行 {@link #testAuth()} 获取子设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li>
|
||||||
* <li>运行以下测试方法:
|
* <li>运行以下测试方法:
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -45,10 +56,12 @@ import static cn.iocoder.yudao.module.iot.gateway.protocol.udp.IotDirectDeviceUd
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
|
public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
|
||||||
|
|
||||||
private static final String SERVER_HOST = "127.0.0.1";
|
|
||||||
private static final int SERVER_PORT = 8093;
|
|
||||||
private static final int TIMEOUT_MS = 5000;
|
private static final int TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
|
// ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
|
||||||
|
private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
|
||||||
|
// private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
|
||||||
|
|
||||||
// ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
|
// ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
|
||||||
private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
|
private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
|
||||||
private static final String DEVICE_NAME = "chazuo-it";
|
private static final String DEVICE_NAME = "chazuo-it";
|
||||||
@@ -66,27 +79,32 @@ public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAuth() throws Exception {
|
public void testAuth() throws Exception {
|
||||||
// 1.1 构建请求
|
// 1.1 构建认证消息
|
||||||
IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);
|
IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);
|
||||||
IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
|
IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
|
||||||
.setClientId(authInfo.getClientId())
|
.setClientId(authInfo.getClientId())
|
||||||
.setUsername(authInfo.getUsername())
|
.setUsername(authInfo.getUsername())
|
||||||
.setPassword(authInfo.getPassword());
|
.setPassword(authInfo.getPassword());
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(IdUtil.fastSimpleUUID(), "auth", authReqDTO, null, null, null);
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
// 1.2 编码
|
||||||
.put("method", "auth")
|
byte[] payload = CODEC.encode(request);
|
||||||
.put("params", authReqDTO)
|
log.info("[testAuth][Codec: {}, 请求消息: {}, 数据包长度: {} 字节]", CODEC.type(), request, payload.length);
|
||||||
.build());
|
if (CODEC instanceof IotTcpBinaryDeviceMessageCodec) {
|
||||||
// 1.2 输出请求
|
log.info("[testAuth][二进制数据包(HEX): {}]", HexUtil.encodeHexStr(payload));
|
||||||
log.info("[testAuth][请求体: {}]", payload);
|
}
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testAuth][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
log.info("[testAuth][请将返回的 token 复制到 TOKEN 常量中]");
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testAuth][响应消息: {}]", response);
|
||||||
|
log.info("[testAuth][请将返回的 token 复制到 TOKEN 常量中]");
|
||||||
|
} else {
|
||||||
|
log.warn("[testAuth][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,27 +115,32 @@ public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testPropertyPost() throws Exception {
|
public void testPropertyPost() throws Exception {
|
||||||
// 1.1 构建请求(UDP 协议:token 放在 params 中)
|
// 1.1 构建属性上报消息(UDP 协议:token 放在 params 中)
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod())
|
IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()
|
||||||
.put("params", withToken(IotDevicePropertyPostReqDTO.of(MapUtil.<String, Object>builder()
|
|
||||||
.put("power", 100)
|
.put("power", 100)
|
||||||
.put("status", "online")
|
.put("status", "online")
|
||||||
.put("temperature", 36.5)
|
.put("temperature", 36.5)
|
||||||
.build())))
|
.build())),
|
||||||
.build());
|
null, null, null);
|
||||||
// 1.2 输出请求
|
// 1.2 编码
|
||||||
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]");
|
log.info("[testPropertyPost][子设备属性上报 - 请求实际由 Gateway 代为转发]");
|
||||||
log.info("[testPropertyPost][请求体: {}]", payload);
|
log.info("[testPropertyPost][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testPropertyPost][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testPropertyPost][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testPropertyPost][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,12 +151,11 @@ public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEventPost() throws Exception {
|
public void testEventPost() throws Exception {
|
||||||
// 1.1 构建请求(UDP 协议:token 放在 params 中)
|
// 1.1 构建事件上报消息(UDP 协议:token 放在 params 中)
|
||||||
String payload = JsonUtils.toJsonString(MapUtil.builder()
|
IotDeviceMessage request = IotDeviceMessage.of(
|
||||||
.put("id", IdUtil.fastSimpleUUID())
|
IdUtil.fastSimpleUUID(),
|
||||||
.put("method", IotDeviceMessageMethodEnum.EVENT_POST.getMethod())
|
IotDeviceMessageMethodEnum.EVENT_POST.getMethod(),
|
||||||
.put("version", "1.0")
|
withToken(IotDeviceEventPostReqDTO.of(
|
||||||
.put("params", withToken(IotDeviceEventPostReqDTO.of(
|
|
||||||
"alarm",
|
"alarm",
|
||||||
MapUtil.<String, Object>builder()
|
MapUtil.<String, Object>builder()
|
||||||
.put("level", "warning")
|
.put("level", "warning")
|
||||||
@@ -141,18 +163,24 @@ public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
|
|||||||
.put("threshold", 40)
|
.put("threshold", 40)
|
||||||
.put("current", 42)
|
.put("current", 42)
|
||||||
.build(),
|
.build(),
|
||||||
System.currentTimeMillis())))
|
System.currentTimeMillis())),
|
||||||
.build());
|
null, null, null);
|
||||||
// 1.2 输出请求
|
// 1.2 编码
|
||||||
|
byte[] payload = CODEC.encode(request);
|
||||||
log.info("[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]");
|
log.info("[testEventPost][子设备事件上报 - 请求实际由 Gateway 代为转发]");
|
||||||
log.info("[testEventPost][请求体: {}]", payload);
|
log.info("[testEventPost][Codec: {}, 请求消息: {}]", CODEC.type(), request);
|
||||||
|
|
||||||
// 2.1 发送请求
|
// 2.1 发送请求
|
||||||
try (DatagramSocket socket = new DatagramSocket()) {
|
try (DatagramSocket socket = new DatagramSocket()) {
|
||||||
socket.setSoTimeout(TIMEOUT_MS);
|
socket.setSoTimeout(TIMEOUT_MS);
|
||||||
String response = sendAndReceive(socket, payload);
|
byte[] responseBytes = sendAndReceive(socket, payload);
|
||||||
// 2.2 输出结果
|
// 2.2 解码响应
|
||||||
log.info("[testEventPost][响应体: {}]", response);
|
if (responseBytes != null) {
|
||||||
|
IotDeviceMessage response = CODEC.decode(responseBytes);
|
||||||
|
log.info("[testEventPost][响应消息: {}]", response);
|
||||||
|
} else {
|
||||||
|
log.warn("[testEventPost][未收到响应]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user