Commit 432e1ed230d853bcd1c92108f5e4525244c8f953

Authored by YunaiV
1 parent d2c000d6

feat:【iot】单元测试:优化单测质量(格式)

Showing 19 changed files with 50 additions and 909 deletions
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotDirectDeviceCoapProtocolIntegrationTest.java
@@ -49,6 +49,7 @@ public class IotDirectDeviceCoapProtocolIntegrationTest { @@ -49,6 +49,7 @@ public class IotDirectDeviceCoapProtocolIntegrationTest {
49 private static final int SERVER_PORT = 5683; 49 private static final int SERVER_PORT = 5683;
50 50
51 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 51 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  52 +
52 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; 53 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
53 private static final String DEVICE_NAME = "small"; 54 private static final String DEVICE_NAME = "small";
54 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 55 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotGatewayDeviceCoapProtocolIntegrationTest.java
@@ -58,6 +58,7 @@ public class IotGatewayDeviceCoapProtocolIntegrationTest { @@ -58,6 +58,7 @@ public class IotGatewayDeviceCoapProtocolIntegrationTest {
58 private static final int SERVER_PORT = 5683; 58 private static final int SERVER_PORT = 5683;
59 59
60 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 60 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
  61 +
61 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; 62 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
62 private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; 63 private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
63 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; 64 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";
@@ -68,6 +69,7 @@ public class IotGatewayDeviceCoapProtocolIntegrationTest { @@ -68,6 +69,7 @@ public class IotGatewayDeviceCoapProtocolIntegrationTest {
68 private static final String GATEWAY_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU"; 69 private static final String GATEWAY_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU";
69 70
70 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 71 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  72 +
71 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 73 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn";
72 private static final String SUB_DEVICE_NAME = "chazuo-it"; 74 private static final String SUB_DEVICE_NAME = "chazuo-it";
73 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 75 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/coap/IotGatewaySubDeviceCoapProtocolIntegrationTest.java
@@ -51,6 +51,7 @@ public class IotGatewaySubDeviceCoapProtocolIntegrationTest { @@ -51,6 +51,7 @@ public class IotGatewaySubDeviceCoapProtocolIntegrationTest {
51 private static final int SERVER_PORT = 5683; 51 private static final int SERVER_PORT = 5683;
52 52
53 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 53 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  54 +
54 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 55 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
55 private static final String DEVICE_NAME = "chazuo-it"; 56 private static final String DEVICE_NAME = "chazuo-it";
56 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 57 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java
@@ -43,6 +43,7 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { @@ -43,6 +43,7 @@ public class IotDirectDeviceHttpProtocolIntegrationTest {
43 private static final int SERVER_PORT = 8092; 43 private static final int SERVER_PORT = 8092;
44 44
45 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 45 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  46 +
46 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; 47 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
47 private static final String DEVICE_NAME = "small"; 48 private static final String DEVICE_NAME = "small";
48 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 49 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java
@@ -52,6 +52,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { @@ -52,6 +52,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest {
52 private static final int SERVER_PORT = 8092; 52 private static final int SERVER_PORT = 8092;
53 53
54 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 54 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
  55 +
55 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; 56 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
56 private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; 57 private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
57 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; 58 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";
@@ -62,6 +63,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { @@ -62,6 +63,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest {
62 private static final String GATEWAY_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU"; 63 private static final String GATEWAY_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU";
63 64
64 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 65 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  66 +
65 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 67 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn";
66 private static final String SUB_DEVICE_NAME = "chazuo-it"; 68 private static final String SUB_DEVICE_NAME = "chazuo-it";
67 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 69 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewaySubDeviceHttpProtocolIntegrationTest.java
@@ -45,6 +45,7 @@ public class IotGatewaySubDeviceHttpProtocolIntegrationTest { @@ -45,6 +45,7 @@ public class IotGatewaySubDeviceHttpProtocolIntegrationTest {
45 private static final int SERVER_PORT = 8092; 45 private static final int SERVER_PORT = 8092;
46 46
47 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 47 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  48 +
48 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 49 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
49 private static final String DEVICE_NAME = "chazuo-it"; 50 private static final String DEVICE_NAME = "chazuo-it";
50 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 51 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotDirectDeviceMqttProtocolIntegrationTest.java
@@ -55,17 +55,18 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { @@ -55,17 +55,18 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
55 private static final int SERVER_PORT = 1883; 55 private static final int SERVER_PORT = 1883;
56 private static final int TIMEOUT_SECONDS = 10; 56 private static final int TIMEOUT_SECONDS = 10;
57 57
  58 + private static Vertx vertx;
  59 +
58 // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== 60 // ===================== 编解码器(MQTT 使用 Alink 协议) =====================
  61 +
59 private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); 62 private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec();
60 63
61 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== 64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) =====================
  65 +
62 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; 66 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
63 private static final String DEVICE_NAME = "small"; 67 private static final String DEVICE_NAME = "small";
64 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 68 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
65 69
66 - // ===================== 全局共享 Vertx 实例 =====================  
67 - private static Vertx vertx;  
68 -  
69 @BeforeAll 70 @BeforeAll
70 public static void setUp() { 71 public static void setUp() {
71 vertx = Vertx.vertx(); 72 vertx = Vertx.vertx();
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewayDeviceMqttProtocolIntegrationTest.java
@@ -63,22 +63,24 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { @@ -63,22 +63,24 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest {
63 private static final int SERVER_PORT = 1883; 63 private static final int SERVER_PORT = 1883;
64 private static final int TIMEOUT_SECONDS = 10; 64 private static final int TIMEOUT_SECONDS = 10;
65 65
  66 + private static Vertx vertx;
  67 +
66 // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== 68 // ===================== 编解码器(MQTT 使用 Alink 协议) =====================
  69 +
67 private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); 70 private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec();
68 71
69 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 72 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
  73 +
70 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; 74 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
71 private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; 75 private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
72 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; 76 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";
73 77
74 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 78 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  79 +
75 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 80 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn";
76 private static final String SUB_DEVICE_NAME = "chazuo-it"; 81 private static final String SUB_DEVICE_NAME = "chazuo-it";
77 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 82 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
78 83
79 - // ===================== 全局共享 Vertx 实例 =====================  
80 - private static Vertx vertx;  
81 -  
82 @BeforeAll 84 @BeforeAll
83 public static void setUp() { 85 public static void setUp() {
84 vertx = Vertx.vertx(); 86 vertx = Vertx.vertx();
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewaySubDeviceMqttProtocolIntegrationTest.java
@@ -57,17 +57,18 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { @@ -57,17 +57,18 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest {
57 private static final int SERVER_PORT = 1883; 57 private static final int SERVER_PORT = 1883;
58 private static final int TIMEOUT_SECONDS = 10; 58 private static final int TIMEOUT_SECONDS = 10;
59 59
  60 + private static Vertx vertx;
  61 +
60 // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== 62 // ===================== 编解码器(MQTT 使用 Alink 协议) =====================
  63 +
61 private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); 64 private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec();
62 65
63 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 66 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  67 +
64 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 68 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
65 private static final String DEVICE_NAME = "chazuo-it"; 69 private static final String DEVICE_NAME = "chazuo-it";
66 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 70 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
67 71
68 - // ===================== 全局共享 Vertx 实例 =====================  
69 - private static Vertx vertx;  
70 -  
71 @BeforeAll 72 @BeforeAll
72 public static void setUp() { 73 public static void setUp() {
73 vertx = Vertx.vertx(); 74 vertx = Vertx.vertx();
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotDirectDeviceTcpProtocolIntegrationTest.java
@@ -57,10 +57,12 @@ public class IotDirectDeviceTcpProtocolIntegrationTest { @@ -57,10 +57,12 @@ public class IotDirectDeviceTcpProtocolIntegrationTest {
57 private static final int TIMEOUT_MS = 5000; 57 private static final int TIMEOUT_MS = 5000;
58 58
59 // ===================== 编解码器选择(修改此处切换 JSON / Binary) ===================== 59 // ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
  60 +
60 // private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec(); 61 // private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
61 private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec(); 62 private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
62 63
63 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== 64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) =====================
  65 +
64 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; 66 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
65 private static final String DEVICE_NAME = "small"; 67 private static final String DEVICE_NAME = "small";
66 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 68 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotGatewayDeviceTcpProtocolIntegrationTest.java
@@ -65,15 +65,18 @@ public class IotGatewayDeviceTcpProtocolIntegrationTest { @@ -65,15 +65,18 @@ public class IotGatewayDeviceTcpProtocolIntegrationTest {
65 private static final int TIMEOUT_MS = 5000; 65 private static final int TIMEOUT_MS = 5000;
66 66
67 // ===================== 编解码器选择(修改此处切换 JSON / Binary) ===================== 67 // ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
  68 +
68 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec(); 69 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
69 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec(); 70 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
70 71
71 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 72 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
  73 +
72 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; 74 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
73 private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; 75 private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
74 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; 76 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";
75 77
76 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 78 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  79 +
77 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 80 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn";
78 private static final String SUB_DEVICE_NAME = "chazuo-it"; 81 private static final String SUB_DEVICE_NAME = "chazuo-it";
79 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 82 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/IotGatewaySubDeviceTcpProtocolIntegrationTest.java
@@ -58,10 +58,12 @@ public class IotGatewaySubDeviceTcpProtocolIntegrationTest { @@ -58,10 +58,12 @@ public class IotGatewaySubDeviceTcpProtocolIntegrationTest {
58 private static final int TIMEOUT_MS = 5000; 58 private static final int TIMEOUT_MS = 5000;
59 59
60 // ===================== 编解码器选择(修改此处切换 JSON / Binary) ===================== 60 // ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
  61 +
61 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec(); 62 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
62 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec(); 63 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
63 64
64 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 65 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  66 +
65 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 67 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
66 private static final String DEVICE_NAME = "chazuo-it"; 68 private static final String DEVICE_NAME = "chazuo-it";
67 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 69 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotDirectDeviceUdpProtocolIntegrationTest.java
@@ -58,10 +58,12 @@ public class IotDirectDeviceUdpProtocolIntegrationTest { @@ -58,10 +58,12 @@ public class IotDirectDeviceUdpProtocolIntegrationTest {
58 private static final int TIMEOUT_MS = 5000; 58 private static final int TIMEOUT_MS = 5000;
59 59
60 // ===================== 编解码器选择(修改此处切换 JSON / Binary) ===================== 60 // ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
  61 +
61 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec(); 62 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
62 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec(); 63 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
63 64
64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 65 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  66 +
65 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; 67 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
66 private static final String DEVICE_NAME = "small"; 68 private static final String DEVICE_NAME = "small";
67 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 69 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotGatewayDeviceUdpProtocolIntegrationTest.java
@@ -64,10 +64,12 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest { @@ -64,10 +64,12 @@ public class IotGatewayDeviceUdpProtocolIntegrationTest {
64 private static final int TIMEOUT_MS = 5000; 64 private static final int TIMEOUT_MS = 5000;
65 65
66 // ===================== 编解码器选择(修改此处切换 JSON / Binary) ===================== 66 // ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
  67 +
67 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec(); 68 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
68 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec(); 69 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
69 70
70 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 71 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
  72 +
71 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; 73 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
72 private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; 74 private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
73 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; 75 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/udp/IotGatewaySubDeviceUdpProtocolIntegrationTest.java
@@ -59,10 +59,12 @@ public class IotGatewaySubDeviceUdpProtocolIntegrationTest { @@ -59,10 +59,12 @@ public class IotGatewaySubDeviceUdpProtocolIntegrationTest {
59 private static final int TIMEOUT_MS = 5000; 59 private static final int TIMEOUT_MS = 5000;
60 60
61 // ===================== 编解码器选择(修改此处切换 JSON / Binary) ===================== 61 // ===================== 编解码器选择(修改此处切换 JSON / Binary) =====================
  62 +
62 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec(); 63 private static final IotDeviceMessageCodec CODEC = new IotTcpJsonDeviceMessageCodec();
63 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec(); 64 // private static final IotDeviceMessageCodec CODEC = new IotTcpBinaryDeviceMessageCodec();
64 65
65 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 66 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  67 +
66 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 68 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
67 private static final String DEVICE_NAME = "chazuo-it"; 69 private static final String DEVICE_NAME = "chazuo-it";
68 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 70 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotDirectDeviceWebSocketProtocolIntegrationTest.java
@@ -55,17 +55,18 @@ public class IotDirectDeviceWebSocketProtocolIntegrationTest { @@ -55,17 +55,18 @@ public class IotDirectDeviceWebSocketProtocolIntegrationTest {
55 private static final String WS_PATH = "/ws"; 55 private static final String WS_PATH = "/ws";
56 private static final int TIMEOUT_SECONDS = 5; 56 private static final int TIMEOUT_SECONDS = 5;
57 57
58 - // 编解码器 58 + private static Vertx vertx;
  59 +
  60 + // ===================== 编解码器选择 =====================
  61 +
59 private static final IotDeviceMessageCodec CODEC = new IotWebSocketJsonDeviceMessageCodec(); 62 private static final IotDeviceMessageCodec CODEC = new IotWebSocketJsonDeviceMessageCodec();
60 63
61 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== 64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) =====================
  65 +
62 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; 66 private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";
63 private static final String DEVICE_NAME = "small"; 67 private static final String DEVICE_NAME = "small";
64 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 68 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
65 69
66 - // Vert.x 实例  
67 - private static Vertx vertx;  
68 -  
69 @BeforeAll 70 @BeforeAll
70 public static void setUp() { 71 public static void setUp() {
71 vertx = Vertx.vertx(); 72 vertx = Vertx.vertx();
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotGatewayDeviceWebSocketProtocolIntegrationTest.java
@@ -63,22 +63,24 @@ public class IotGatewayDeviceWebSocketProtocolIntegrationTest { @@ -63,22 +63,24 @@ public class IotGatewayDeviceWebSocketProtocolIntegrationTest {
63 private static final String WS_PATH = "/ws"; 63 private static final String WS_PATH = "/ws";
64 private static final int TIMEOUT_SECONDS = 5; 64 private static final int TIMEOUT_SECONDS = 5;
65 65
66 - // 编解码器 66 + private static Vertx vertx;
  67 +
  68 + // ===================== 编解码器选择 =====================
  69 +
67 private static final IotDeviceMessageCodec CODEC = new IotWebSocketJsonDeviceMessageCodec(); 70 private static final IotDeviceMessageCodec CODEC = new IotWebSocketJsonDeviceMessageCodec();
68 71
69 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 72 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
  73 +
70 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; 74 private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";
71 private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; 75 private static final String GATEWAY_DEVICE_NAME = "sub-ddd";
72 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; 76 private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";
73 77
74 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 78 // ===================== 子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  79 +
75 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 80 private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn";
76 private static final String SUB_DEVICE_NAME = "chazuo-it"; 81 private static final String SUB_DEVICE_NAME = "chazuo-it";
77 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 82 private static final String SUB_DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
78 83
79 - // Vert.x 实例  
80 - private static Vertx vertx;  
81 -  
82 @BeforeAll 84 @BeforeAll
83 public static void setUp() { 85 public static void setUp() {
84 vertx = Vertx.vertx(); 86 vertx = Vertx.vertx();
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/websocket/IotGatewaySubDeviceWebSocketProtocolIntegrationTest.java
@@ -56,17 +56,18 @@ public class IotGatewaySubDeviceWebSocketProtocolIntegrationTest { @@ -56,17 +56,18 @@ public class IotGatewaySubDeviceWebSocketProtocolIntegrationTest {
56 private static final String WS_PATH = "/ws"; 56 private static final String WS_PATH = "/ws";
57 private static final int TIMEOUT_SECONDS = 5; 57 private static final int TIMEOUT_SECONDS = 5;
58 58
59 - // 编解码器 59 + private static Vertx vertx;
  60 +
  61 + // ===================== 编解码器选择 =====================
  62 +
60 private static final IotDeviceMessageCodec CODEC = new IotWebSocketJsonDeviceMessageCodec(); 63 private static final IotDeviceMessageCodec CODEC = new IotWebSocketJsonDeviceMessageCodec();
61 64
62 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 65 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
  66 +
63 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn"; 67 private static final String PRODUCT_KEY = "jAufEMTF1W6wnPhn";
64 private static final String DEVICE_NAME = "chazuo-it"; 68 private static final String DEVICE_NAME = "chazuo-it";
65 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af"; 69 private static final String DEVICE_SECRET = "d46ef9b28ab14238b9c00a3a668032af";
66 70
67 - // Vert.x 实例  
68 - private static Vertx vertx;  
69 -  
70 @BeforeAll 71 @BeforeAll
71 public static void setUp() { 72 public static void setUp() {
72 vertx = Vertx.vertx(); 73 vertx = Vertx.vertx();
yudao-module-iot/yudao-module-iot-gateway/src/test/resources/mqtt-websocket-test-client.html deleted
1 -<!DOCTYPE html>  
2 -<html lang="zh-CN">  
3 -<head>  
4 - <meta charset="UTF-8">  
5 - <meta content="width=device-width, initial-scale=1.0" name="viewport">  
6 - <title>MQTT WebSocket 测试客户端</title>  
7 - <style>  
8 - * {  
9 - margin: 0;  
10 - padding: 0;  
11 - box-sizing: border-box;  
12 - }  
13 -  
14 - body {  
15 - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;  
16 - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);  
17 - padding: 20px;  
18 - min-height: 100vh;  
19 - }  
20 -  
21 - .container {  
22 - max-width: 1200px;  
23 - margin: 0 auto;  
24 - background: white;  
25 - border-radius: 10px;  
26 - box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);  
27 - overflow: hidden;  
28 - }  
29 -  
30 - .header {  
31 - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);  
32 - color: white;  
33 - padding: 30px;  
34 - text-align: center;  
35 - }  
36 -  
37 - .header h1 {  
38 - font-size: 28px;  
39 - margin-bottom: 10px;  
40 - }  
41 -  
42 - .header p {  
43 - opacity: 0.9;  
44 - }  
45 -  
46 - .info-box {  
47 - background: #f8f9fa;  
48 - border-left: 4px solid #667eea;  
49 - padding: 15px;  
50 - margin: 20px;  
51 - border-radius: 5px;  
52 - }  
53 -  
54 - .info-box h3 {  
55 - color: #667eea;  
56 - margin-bottom: 10px;  
57 - font-size: 16px;  
58 - }  
59 -  
60 - .info-box ul {  
61 - margin-left: 20px;  
62 - color: #666;  
63 - font-size: 14px;  
64 - line-height: 1.6;  
65 - }  
66 -  
67 - .info-box code {  
68 - background: #e9ecef;  
69 - padding: 2px 6px;  
70 - border-radius: 3px;  
71 - font-family: 'Courier New', monospace;  
72 - color: #d63384;  
73 - }  
74 -  
75 - .content {  
76 - display: grid;  
77 - grid-template-columns: 1fr 1fr;  
78 - gap: 20px;  
79 - padding: 30px;  
80 - }  
81 -  
82 - .panel {  
83 - background: #f8f9fa;  
84 - border-radius: 8px;  
85 - padding: 20px;  
86 - }  
87 -  
88 - .panel h2 {  
89 - color: #667eea;  
90 - margin-bottom: 20px;  
91 - font-size: 20px;  
92 - border-bottom: 2px solid #667eea;  
93 - padding-bottom: 10px;  
94 - }  
95 -  
96 - .form-group {  
97 - margin-bottom: 15px;  
98 - }  
99 -  
100 - .form-group label {  
101 - display: block;  
102 - margin-bottom: 5px;  
103 - color: #333;  
104 - font-weight: 500;  
105 - }  
106 -  
107 - .form-group input,  
108 - .form-group select,  
109 - .form-group textarea {  
110 - width: 100%;  
111 - padding: 10px;  
112 - border: 1px solid #ddd;  
113 - border-radius: 4px;  
114 - font-size: 14px;  
115 - }  
116 -  
117 - .form-group input:focus,  
118 - .form-group select:focus,  
119 - .form-group textarea:focus {  
120 - outline: none;  
121 - border-color: #667eea;  
122 - }  
123 -  
124 - .form-group textarea {  
125 - resize: vertical;  
126 - min-height: 80px;  
127 - font-family: monospace;  
128 - }  
129 -  
130 - .btn {  
131 - padding: 10px 20px;  
132 - border: none;  
133 - border-radius: 4px;  
134 - cursor: pointer;  
135 - font-size: 14px;  
136 - font-weight: 500;  
137 - transition: all 0.3s;  
138 - }  
139 -  
140 - .btn-primary {  
141 - background: #667eea;  
142 - color: white;  
143 - }  
144 -  
145 - .btn-primary:hover {  
146 - background: #5568d3;  
147 - }  
148 -  
149 - .btn-success {  
150 - background: #28a745;  
151 - color: white;  
152 - }  
153 -  
154 - .btn-success:hover {  
155 - background: #218838;  
156 - }  
157 -  
158 - .btn-danger {  
159 - background: #dc3545;  
160 - color: white;  
161 - }  
162 -  
163 - .btn-danger:hover {  
164 - background: #c82333;  
165 - }  
166 -  
167 - .btn-warning {  
168 - background: #ffc107;  
169 - color: #333;  
170 - }  
171 -  
172 - .btn-warning:hover {  
173 - background: #e0a800;  
174 - }  
175 -  
176 - .btn-group {  
177 - display: flex;  
178 - gap: 10px;  
179 - margin-top: 15px;  
180 - }  
181 -  
182 - .status {  
183 - padding: 10px;  
184 - border-radius: 4px;  
185 - margin-bottom: 20px;  
186 - font-weight: 500;  
187 - }  
188 -  
189 - .status.disconnected {  
190 - background: #f8d7da;  
191 - color: #721c24;  
192 - }  
193 -  
194 - .status.connected {  
195 - background: #d4edda;  
196 - color: #155724;  
197 - }  
198 -  
199 - .status.connecting {  
200 - background: #fff3cd;  
201 - color: #856404;  
202 - }  
203 -  
204 - .log-area {  
205 - background: #1e1e1e;  
206 - color: #d4d4d4;  
207 - padding: 15px;  
208 - border-radius: 4px;  
209 - height: 400px;  
210 - overflow-y: auto;  
211 - font-family: 'Courier New', monospace;  
212 - font-size: 12px;  
213 - line-height: 1.6;  
214 - }  
215 -  
216 - .log-entry {  
217 - margin-bottom: 5px;  
218 - }  
219 -  
220 - .log-entry.info {  
221 - color: #4ec9b0;  
222 - }  
223 -  
224 - .log-entry.success {  
225 - color: #6a9955;  
226 - }  
227 -  
228 - .log-entry.error {  
229 - color: #f48771;  
230 - }  
231 -  
232 - .log-entry.warning {  
233 - color: #dcdcaa;  
234 - }  
235 -  
236 - .stats {  
237 - display: grid;  
238 - grid-template-columns: repeat(3, 1fr);  
239 - gap: 10px;  
240 - margin-top: 15px;  
241 - }  
242 -  
243 - .stat-item {  
244 - background: white;  
245 - padding: 15px;  
246 - border-radius: 4px;  
247 - text-align: center;  
248 - }  
249 -  
250 - .stat-item .value {  
251 - font-size: 24px;  
252 - font-weight: bold;  
253 - color: #667eea;  
254 - }  
255 -  
256 - .stat-item .label {  
257 - font-size: 12px;  
258 - color: #666;  
259 - margin-top: 5px;  
260 - }  
261 -  
262 - @media (max-width: 768px) {  
263 - .content {  
264 - grid-template-columns: 1fr;  
265 - }  
266 - }  
267 - </style>  
268 -</head>  
269 -<body>  
270 -<div class="container">  
271 - <div class="header">  
272 - <h1>🚀 MQTT WebSocket 测试客户端</h1>  
273 - <p>RuoYi-Vue-Pro IoT 模块 - MQTT over WebSocket 在线测试工具</p>  
274 - </div>  
275 -  
276 - <!-- 协议格式说明 -->  
277 - <div class="info-box">  
278 - <h3>📌 标准协议格式说明</h3>  
279 - <ul>  
280 - <li><strong>Topic 格式:</strong><code>/sys/{productKey}/{deviceName}/thing/property/post</code></li>  
281 - <li><strong>Client ID 格式:</strong><code>{productKey}.{deviceName}</code> 例如:<code>zOXKLvHjUqTo7ipD.ceshi001</code>  
282 - </li>  
283 - <li><strong>Username 格式:</strong><code>{deviceName}&{productKey}</code> 例如:<code>ceshi001&zOXKLvHjUqTo7ipD</code>  
284 - </li>  
285 - <li><strong>消息格式(Alink 协议):</strong>  
286 - <pre style="background: #e9ecef; padding: 10px; border-radius: 5px; margin-top: 5px; overflow-x: auto;">  
287 -{  
288 - "id": "消息 ID(唯一标识)",  
289 - "version": "1.0",  
290 - "method": "thing.property.post",  
291 - "params": {  
292 - "temperature": 25.5,  
293 - "humidity": 60  
294 - }  
295 -}</pre>  
296 - </li>  
297 - <li><strong>常用 Topic(下行 - 服务端推送):</strong>  
298 - <ul style="margin-top: 5px;">  
299 - <li>属性设置:<code>/sys/{pk}/{dn}/thing/property/set</code></li>  
300 - <li>服务调用:<code>/sys/{pk}/{dn}/thing/service/invoke</code></li>  
301 - <li>配置推送:<code>/sys/{pk}/{dn}/thing/config/push</code></li>  
302 - <li>OTA 升级:<code>/sys/{pk}/{dn}/thing/ota/upgrade</code></li>  
303 - </ul>  
304 - </li>  
305 - <li><strong>常用 Topic(上行 - 设备上报):</strong>  
306 - <ul style="margin-top: 5px;">  
307 - <li>状态更新:<code>/sys/{pk}/{dn}/thing/state/update</code></li>  
308 - <li>属性上报:<code>/sys/{pk}/{dn}/thing/property/post</code></li>  
309 - <li>事件上报:<code>/sys/{pk}/{dn}/thing/event/post</code></li>  
310 - <li>OTA 进度:<code>/sys/{pk}/{dn}/thing/ota/progress</code></li>  
311 - </ul>  
312 - </li>  
313 - </ul>  
314 - </div>  
315 -  
316 - <div class="content">  
317 - <!-- 连接配置面板 -->  
318 - <div class="panel">  
319 - <h2>📡 连接配置</h2>  
320 -  
321 - <div class="status disconnected" id="statusBar">  
322 - ⚫ 未连接  
323 - </div>  
324 -  
325 - <div class="form-group">  
326 - <label>服务器地址</label>  
327 - <input id="serverUrl" placeholder="ws://host:port/path" type="text" value="ws://localhost:8083/mqtt">  
328 - <small style="color: #666; font-size: 12px;">WebSocket 地址,支持 ws:// 和 wss://</small>  
329 - </div>  
330 -  
331 - <div class="form-group">  
332 - <label>Client ID</label>  
333 - <input id="clientId" placeholder="设备客户端 ID" type="text" value="fqTn4Afs982Nak4N.jiali001">  
334 - <small style="color: #666; font-size: 12px;">格式:{productKey}.{deviceName}</small>  
335 - </div>  
336 -  
337 - <div class="form-group">  
338 - <label>Username</label>  
339 - <input id="username" placeholder="用户名" type="text" value="jiali001&fqTn4Afs982Nak4N">  
340 - <small style="color: #666; font-size: 12px;">格式:{deviceName}&{productKey}</small>  
341 - </div>  
342 -  
343 - <div class="form-group">  
344 - <label>Password</label>  
345 - <input id="password" placeholder="设备密钥"  
346 - type="password" value="ae10188f93febbb6b37bd57f463b2a795ae2800fab8933aef75d3c6422873f28">  
347 - <small style="color: #666; font-size: 12px;">设备的认证密钥(Device Secret)</small>  
348 - </div>  
349 -  
350 - <div class="btn-group">  
351 - <button class="btn btn-success" id="connectBtn" onclick="connect()">🔌 连接</button>  
352 - <button class="btn btn-danger" disabled id="disconnectBtn" onclick="disconnect()">🔌 断开</button>  
353 - <button class="btn btn-warning" onclick="clearLogs()">🗑️ 清空日志</button>  
354 - </div>  
355 -  
356 - <!-- 统计信息 -->  
357 - <div class="stats">  
358 - <div class="stat-item">  
359 - <div class="value" id="sentCount">0</div>  
360 - <div class="label">发送消息数</div>  
361 - </div>  
362 - <div class="stat-item">  
363 - <div class="value" id="receivedCount">0</div>  
364 - <div class="label">接收消息数</div>  
365 - </div>  
366 - <div class="stat-item">  
367 - <div class="value" id="errorCount">0</div>  
368 - <div class="label">错误次数</div>  
369 - </div>  
370 - </div>  
371 - </div>  
372 -  
373 - <!-- 消息发布面板 -->  
374 - <div class="panel">  
375 - <h2>📤 消息发布</h2>  
376 -  
377 - <div class="form-group">  
378 - <label>快捷主题选择(上行消息 - 设备 → 服务端)</label>  
379 - <select id="quickPublishTopicSelect" onchange="selectQuickPublishTopic()"  
380 - style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">  
381 - <option value="">-- 选择上行消息类型 --</option>  
382 - <option value="thing.state.update">设备状态更新 (thing.state.update)</option>  
383 - <option value="thing.property.post">属性上报 (thing.property.post)</option>  
384 - <option value="thing.event.post">事件上报 (thing.event.post)</option>  
385 - <option value="thing.ota.progress">OTA 升级进度 (thing.ota.progress)</option>  
386 - </select>  
387 - </div>  
388 -  
389 - <div class="form-group">  
390 - <label>主题 (Topic)</label>  
391 - <input id="pubTopic" placeholder="消息主题,格式:/sys/{productKey}/{deviceName}/thing/property/post" type="text"  
392 - value="/sys/fqTn4Afs982Nak4N/jiali001/thing/property/post">  
393 - <small style="color: #666; font-size: 12px;">标准格式:/sys/{productKey}/{deviceName}/thing/property/post</small>  
394 - </div>  
395 -  
396 - <div class="form-group">  
397 - <label>QoS 级别</label>  
398 - <select id="pubQos">  
399 - <option value="0">0 - 最多一次</option>  
400 - <option selected value="1">1 - 至少一次</option>  
401 - <option value="2">2 - 刚好一次</option>  
402 - </select>  
403 - </div>  
404 -  
405 - <div class="form-group">  
406 - <label>消息内容 (JSON - Alink 协议格式)</label>  
407 - <textarea id="pubMessage" placeholder='Alink 协议格式消息'>{  
408 - "id": "123456789",  
409 - "version": "1.0",  
410 - "method": "thing.property.post",  
411 - "params": {  
412 - "temperature": 25.5,  
413 - "humidity": 60  
414 - }  
415 -}</textarea>  
416 - <small style="color: #666; font-size: 12px;">  
417 - Alink 协议格式:id(消息 ID)、version(协议版本)、method(方法)、params(参数)  
418 - </small>  
419 - </div>  
420 -  
421 - <div class="btn-group">  
422 - <button class="btn btn-primary" onclick="publish()">📤 发布消息</button>  
423 - <button class="btn btn-success" onclick="publishSampleData()">📊 发送样例数据</button>  
424 - </div>  
425 -  
426 - <h2 style="margin-top: 30px;">📥 主题订阅</h2>  
427 -  
428 - <div class="form-group">  
429 - <label>快捷主题选择(下行消息 - 服务端 → 设备)</label>  
430 - <select id="quickTopicSelect" onchange="selectQuickTopic()"  
431 - style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 5px;">  
432 - <option value="">-- 选择下行消息类型 --</option>  
433 - <optgroup label="📥 下行消息">  
434 - <option value="thing.property.set">属性设置 (thing.property.set)</option>  
435 - <option value="thing.service.invoke">服务调用 (thing.service.invoke)</option>  
436 - <option value="thing.config.push">配置推送 (thing.config.push)</option>  
437 - <option value="thing.ota.upgrade">OTA 固件推送 (thing.ota.upgrade)</option>  
438 - </optgroup>  
439 - <optgroup label="🔄 回复主题(上行消息的回复)">  
440 - <option value="thing.property.post_reply">属性上报回复 (thing.property.post_reply)</option>  
441 - <option value="thing.event.post_reply">事件上报回复 (thing.event.post_reply)</option>  
442 - </optgroup>  
443 - <optgroup label="🔧 通配符订阅">  
444 - <option value="wildcard_all">订阅所有主题 (/sys/+/+/#)</option>  
445 - <option value="wildcard_thing">订阅所有 thing 主题 (/sys/+/+/thing/#)</option>  
446 - <option value="wildcard_reply">订阅所有回复主题 (/sys/+/+/#_reply)</option>  
447 - </optgroup>  
448 - </select>  
449 - </div>  
450 -  
451 - <div class="form-group">  
452 - <label>订阅主题</label>  
453 - <input id="subTopic" placeholder="订阅主题,格式:/sys/{productKey}/{deviceName}/thing/property/set" type="text"  
454 - value="/sys/fqTn4Afs982Nak4N/jiali001/thing/property/set">  
455 - <small style="color: #666; font-size: 12px;">标准格式:/sys/{productKey}/{deviceName}/thing/method 或使用通配符  
456 - /sys/+/+/#</small>  
457 - </div>  
458 -  
459 - <div class="form-group">  
460 - <label>QoS 级别</label>  
461 - <select id="subQos">  
462 - <option value="0">0 - 最多一次</option>  
463 - <option selected value="1">1 - 至少一次</option>  
464 - <option value="2">2 - 刚好一次</option>  
465 - </select>  
466 - </div>  
467 -  
468 - <div class="btn-group">  
469 - <button class="btn btn-primary" onclick="subscribe()">📥 订阅</button>  
470 - <button class="btn btn-danger" onclick="unsubscribe()">❌ 取消订阅</button>  
471 - </div>  
472 - </div>  
473 -  
474 - <!-- 日志面板 -->  
475 - <div class="panel" style="grid-column: 1 / -1;">  
476 - <h2>📝 日志输出</h2>  
477 - <div class="log-area" id="logArea"></div>  
478 - </div>  
479 - </div>  
480 -</div>  
481 -  
482 -<!-- 使用 MQTT.js 库 -->  
483 -<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>  
484 -  
485 -<script>  
486 - let client = null;  
487 - let sentCount = 0;  
488 - let receivedCount = 0;  
489 - let errorCount = 0;  
490 -  
491 - // 添加日志  
492 - function addLog(message, type = 'info') {  
493 - const logArea = document.getElementById('logArea');  
494 - const timestamp = new Date().toLocaleTimeString();  
495 - const logEntry = document.createElement('div');  
496 - logEntry.className = `log-entry ${type}`;  
497 - logEntry.textContent = `[${timestamp}] ${message}`;  
498 - logArea.appendChild(logEntry);  
499 - logArea.scrollTop = logArea.scrollHeight;  
500 - }  
501 -  
502 - // 更新状态栏  
503 - function updateStatus(status, text) {  
504 - const statusBar = document.getElementById('statusBar');  
505 - statusBar.className = `status ${status}`;  
506 -  
507 - const icons = {  
508 - 'disconnected': '⚫',  
509 - 'connecting': '🟡',  
510 - 'connected': '🟢'  
511 - };  
512 -  
513 - statusBar.textContent = `${icons[status]} ${text}`;  
514 - }  
515 -  
516 - // 更新统计信息  
517 - function updateStats() {  
518 - document.getElementById('sentCount').textContent = sentCount;  
519 - document.getElementById('receivedCount').textContent = receivedCount;  
520 - document.getElementById('errorCount').textContent = errorCount;  
521 - }  
522 -  
523 - // 连接到服务器  
524 - function connect() {  
525 - const serverUrl = document.getElementById('serverUrl').value;  
526 - const clientId = document.getElementById('clientId').value;  
527 - const username = document.getElementById('username').value;  
528 - const password = document.getElementById('password').value;  
529 -  
530 - if (!serverUrl || !clientId) {  
531 - addLog('❌ 请填写服务器地址和 Client ID', 'error');  
532 - errorCount++;  
533 - updateStats();  
534 - return;  
535 - }  
536 -  
537 - updateStatus('connecting', '正在连接...');  
538 - addLog(`🔄 正在连接到 ${serverUrl}...`, 'info');  
539 -  
540 - const options = {  
541 - clientId: clientId,  
542 - username: username,  
543 - password: password,  
544 - clean: true,  
545 - reconnectPeriod: 5000,  
546 - connectTimeout: 30000,  
547 - };  
548 -  
549 - client = mqtt.connect(serverUrl, options);  
550 -  
551 - // 连接成功  
552 - client.on('connect', () => {  
553 - updateStatus('connected', '已连接');  
554 - addLog('✅ 连接成功!', 'success');  
555 - document.getElementById('connectBtn').disabled = true;  
556 - document.getElementById('disconnectBtn').disabled = false;  
557 - });  
558 -  
559 - // 接收消息  
560 - client.on('message', (topic, message) => {  
561 - receivedCount++;  
562 - updateStats();  
563 - addLog(`📥 收到消息 [${topic}]: ${message.toString()}`, 'success');  
564 - });  
565 -  
566 - // 连接错误  
567 - client.on('error', (error) => {  
568 - errorCount++;  
569 - updateStats();  
570 - addLog(`❌ 连接错误: ${error.message}`, 'error');  
571 - });  
572 -  
573 - // 断开连接  
574 - client.on('close', () => {  
575 - updateStatus('disconnected', '未连接');  
576 - addLog('🔌 连接已断开', 'warning');  
577 - document.getElementById('connectBtn').disabled = false;  
578 - document.getElementById('disconnectBtn').disabled = true;  
579 - });  
580 -  
581 - // 离线  
582 - client.on('offline', () => {  
583 - updateStatus('disconnected', '离线');  
584 - addLog('⚠️ 客户端离线', 'warning');  
585 - });  
586 -  
587 - // 重连  
588 - client.on('reconnect', () => {  
589 - updateStatus('connecting', '正在重连...');  
590 - addLog('🔄 正在重连...', 'info');  
591 - });  
592 - }  
593 -  
594 - // 断开连接  
595 - function disconnect() {  
596 - if (client) {  
597 - client.end();  
598 - addLog('👋 主动断开连接', 'info');  
599 - }  
600 - }  
601 -  
602 - // 发布消息  
603 - function publish() {  
604 - if (!client || !client.connected) {  
605 - addLog('❌ 请先连接到服务器', 'error');  
606 - errorCount++;  
607 - updateStats();  
608 - return;  
609 - }  
610 -  
611 - const topic = document.getElementById('pubTopic').value;  
612 - const qos = parseInt(document.getElementById('pubQos').value);  
613 - const message = document.getElementById('pubMessage').value;  
614 -  
615 - if (!topic || !message) {  
616 - addLog('❌ 请填写主题和消息内容', 'error');  
617 - errorCount++;  
618 - updateStats();  
619 - return;  
620 - }  
621 -  
622 - // 验证 JSON 格式  
623 - try {  
624 - JSON.parse(message);  
625 - } catch (e) {  
626 - addLog('⚠️ 消息不是有效的 JSON 格式,将作为纯文本发送', 'warning');  
627 - }  
628 -  
629 - client.publish(topic, message, {qos: qos}, (error) => {  
630 - if (error) {  
631 - errorCount++;  
632 - updateStats();  
633 - addLog(`❌ 发布失败: ${error.message}`, 'error');  
634 - } else {  
635 - sentCount++;  
636 - updateStats();  
637 - addLog(`📤 消息已发布 [${topic}] (QoS ${qos})`, 'success');  
638 - }  
639 - });  
640 - }  
641 -  
642 - // 发送样例数据  
643 - function publishSampleData() {  
644 - // 使用 Alink 协议格式的样例数据  
645 - const sampleData = {  
646 - id: Date.now().toString(),  
647 - version: "1.0",  
648 - method: "thing.property.post",  
649 - params: {  
650 - temperature: parseFloat((20 + Math.random() * 10).toFixed(2)),  
651 - humidity: parseFloat((50 + Math.random() * 20).toFixed(2)),  
652 - pressure: parseFloat((1000 + Math.random() * 50).toFixed(2))  
653 - }  
654 - };  
655 -  
656 - document.getElementById('pubMessage').value = JSON.stringify(sampleData, null, 2);  
657 - addLog('样例数据已生成(Alink 协议格式)', 'info');  
658 - publish();  
659 - }  
660 -  
661 - // 获取 productKey 和 deviceName  
662 - function getDeviceInfo() {  
663 - const clientId = document.getElementById('clientId').value;  
664 - const parts = clientId.split('.');  
665 -  
666 - if (parts.length !== 2) {  
667 - addLog('❌ Client ID 格式不正确(应为 {productKey}.{deviceName}),无法生成主题', 'error');  
668 - return null;  
669 - }  
670 -  
671 - return {  
672 - productKey: parts[0],  
673 - deviceName: parts[1]  
674 - };  
675 - }  
676 -  
677 - // 快捷主题选择(消息发布 - 上行消息)  
678 - function selectQuickPublishTopic() {  
679 - const select = document.getElementById('quickPublishTopicSelect');  
680 - const selectedValue = select.value;  
681 -  
682 - console.log('[selectQuickPublishTopic] 选择的值:', selectedValue);  
683 -  
684 - if (!selectedValue) {  
685 - return;  
686 - }  
687 -  
688 - const deviceInfo = getDeviceInfo();  
689 - if (!deviceInfo) {  
690 - return;  
691 - }  
692 -  
693 - console.log('[selectQuickPublishTopic] 设备信息:', deviceInfo);  
694 -  
695 - // 构建标准主题,将枚举中的点号替换为斜杠  
696 - // 例如:thing.property.post -> /sys/{pk}/{dn}/thing/property/post  
697 - const topic = `/sys/${deviceInfo.productKey}/${deviceInfo.deviceName}/${selectedValue.replace(/\./g, '/')}`;  
698 -  
699 - console.log('[selectQuickPublishTopic] 生成的主题:', topic);  
700 -  
701 - const pubTopicInput = document.getElementById('pubTopic');  
702 - pubTopicInput.value = topic;  
703 -  
704 - console.log('[selectQuickPublishTopic] 输入框的值已设置为:', pubTopicInput.value);  
705 -  
706 - addLog(`📋 已选择发布主题: ${topic}`, 'info');  
707 -  
708 - // 需要 reply 的消息类型(不在 REPLY_DISABLED 列表中)  
709 - const needsReply = [  
710 - 'thing.property.post',  
711 - 'thing.event.post'  
712 - ];  
713 -  
714 - // 如果需要 reply,自动订阅 reply 主题  
715 - if (needsReply.includes(selectedValue)) {  
716 - const replyTopic = `${topic}_reply`;  
717 -  
718 - if (client && client.connected) {  
719 - // 自动订阅 reply 主题  
720 - client.subscribe(replyTopic, {qos: 1}, (err) => {  
721 - if (!err) {  
722 - addLog(`✅ 已自动订阅回复主题: ${replyTopic}`, 'success');  
723 - } else {  
724 - addLog(`❌ 自动订阅回复主题失败: ${err.message}`, 'error');  
725 - }  
726 - });  
727 - } else {  
728 - addLog(`💡 提示: 该消息需要订阅回复主题 ${replyTopic}`, 'warning');  
729 - }  
730 - }  
731 -  
732 - // 重置下拉框到默认选项  
733 - select.selectedIndex = 0;  
734 - console.log('[selectQuickPublishTopic] 下拉框已重置');  
735 - }  
736 -  
737 - // 快捷主题选择(主题订阅 - 下行消息)  
738 - function selectQuickTopic() {  
739 - const select = document.getElementById('quickTopicSelect');  
740 - const selectedValue = select.value;  
741 -  
742 - console.log('[selectQuickTopic] 选择的值:', selectedValue);  
743 -  
744 - if (!selectedValue) {  
745 - return;  
746 - }  
747 -  
748 - const subTopicInput = document.getElementById('subTopic');  
749 -  
750 - // 处理通配符订阅  
751 - if (selectedValue === 'wildcard_all') {  
752 - subTopicInput.value = '/sys/+/+/#';  
753 - addLog('📋 已选择订阅主题: /sys/+/+/#(订阅所有主题)', 'info');  
754 - console.log('[selectQuickTopic] 输入框的值已设置为:', subTopicInput.value);  
755 - select.selectedIndex = 0;  
756 - console.log('[selectQuickTopic] 下拉框已重置');  
757 - return;  
758 - } else if (selectedValue === 'wildcard_thing') {  
759 - subTopicInput.value = '/sys/+/+/thing/#';  
760 - addLog('📋 已选择订阅主题: /sys/+/+/thing/#(订阅所有 thing 主题)', 'info');  
761 - console.log('[selectQuickTopic] 输入框的值已设置为:', subTopicInput.value);  
762 - select.selectedIndex = 0;  
763 - console.log('[selectQuickTopic] 下拉框已重置');  
764 - return;  
765 - } else if (selectedValue === 'wildcard_reply') {  
766 - const deviceInfo = getDeviceInfo();  
767 - if (!deviceInfo) {  
768 - select.selectedIndex = 0;  
769 - return;  
770 - }  
771 - subTopicInput.value = `/sys/${deviceInfo.productKey}/${deviceInfo.deviceName}/#_reply`;  
772 - addLog(`📋 已选择订阅主题: /sys/${deviceInfo.productKey}/${deviceInfo.deviceName}/#_reply(订阅所有回复主题)`, 'info');  
773 - console.log('[selectQuickTopic] 输入框的值已设置为:', subTopicInput.value);  
774 - select.selectedIndex = 0;  
775 - console.log('[selectQuickTopic] 下拉框已重置');  
776 - return;  
777 - }  
778 -  
779 - const deviceInfo = getDeviceInfo();  
780 - if (!deviceInfo) {  
781 - select.selectedIndex = 0;  
782 - return;  
783 - }  
784 -  
785 - console.log('[selectQuickTopic] 设备信息:', deviceInfo);  
786 -  
787 - // 构建标准主题,将枚举中的点号替换为斜杠  
788 - // 例如:thing.property.set -> /sys/{pk}/{dn}/thing/property/set  
789 - const topic = `/sys/${deviceInfo.productKey}/${deviceInfo.deviceName}/${selectedValue.replace(/\./g, '/')}`;  
790 -  
791 - console.log('[selectQuickTopic] 生成的主题:', topic);  
792 -  
793 - subTopicInput.value = topic;  
794 - addLog(`📋 已选择订阅主题: ${topic}`, 'info');  
795 -  
796 - console.log('[selectQuickTopic] 输入框的值已设置为:', subTopicInput.value);  
797 -  
798 - // 重置下拉框到默认选项  
799 - select.selectedIndex = 0;  
800 - console.log('[selectQuickTopic] 下拉框已重置');  
801 - }  
802 -  
803 - // 订阅主题  
804 - function subscribe() {  
805 - if (!client || !client.connected) {  
806 - addLog('❌ 请先连接到服务器', 'error');  
807 - errorCount++;  
808 - updateStats();  
809 - return;  
810 - }  
811 -  
812 - const topic = document.getElementById('subTopic').value;  
813 - const qos = parseInt(document.getElementById('subQos').value);  
814 -  
815 - if (!topic) {  
816 - addLog('❌ 请填写订阅主题', 'error');  
817 - errorCount++;  
818 - updateStats();  
819 - return;  
820 - }  
821 -  
822 - client.subscribe(topic, {qos: qos}, (error) => {  
823 - if (error) {  
824 - errorCount++;  
825 - updateStats();  
826 - addLog(`❌ 订阅失败: ${error.message}`, 'error');  
827 - } else {  
828 - addLog(`📥 已订阅主题 [${topic}] (QoS ${qos})`, 'success');  
829 - }  
830 - });  
831 - }  
832 -  
833 - // 取消订阅  
834 - function unsubscribe() {  
835 - if (!client || !client.connected) {  
836 - addLog('❌ 请先连接到服务器', 'error');  
837 - errorCount++;  
838 - updateStats();  
839 - return;  
840 - }  
841 -  
842 - const topic = document.getElementById('subTopic').value;  
843 -  
844 - if (!topic) {  
845 - addLog('❌ 请填写要取消的订阅主题', 'error');  
846 - errorCount++;  
847 - updateStats();  
848 - return;  
849 - }  
850 -  
851 - client.unsubscribe(topic, (error) => {  
852 - if (error) {  
853 - errorCount++;  
854 - updateStats();  
855 - addLog(`❌ 取消订阅失败: ${error.message}`, 'error');  
856 - } else {  
857 - addLog(`❌ 已取消订阅 [${topic}]`, 'info');  
858 - }  
859 - });  
860 - }  
861 -  
862 - // 清空日志  
863 - function clearLogs() {  
864 - document.getElementById('logArea').innerHTML = '';  
865 - sentCount = 0;  
866 - receivedCount = 0;  
867 - errorCount = 0;  
868 - updateStats();  
869 - addLog('🗑️ 日志已清空', 'info');  
870 - }  
871 -  
872 - // 页面加载完成  
873 - window.onload = function () {  
874 - addLog('👋 欢迎使用 MQTT WebSocket 测试客户端!', 'success');  
875 - addLog('📚 请配置连接参数后点击"连接"按钮', 'info');  
876 - };  
877 -  
878 - // 页面关闭前断开连接  
879 - window.onbeforeunload = function () {  
880 - if (client && client.connected) {  
881 - client.end();  
882 - }  
883 - };  
884 -</script>  
885 -</body>  
886 -</html>  
887 -  
888 -