【同步】BOOT 和 CLOUD 的功能(IoT)
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
package cn.iocoder.yudao.module.iot.core.biz;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.*;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;
|
||||
@@ -50,4 +47,12 @@ public interface IotDeviceCommonApi {
|
||||
*/
|
||||
CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 获取 Modbus 设备配置列表
|
||||
*
|
||||
* @param listReqDTO 查询参数
|
||||
* @return Modbus 设备配置列表
|
||||
*/
|
||||
CommonResult<List<IotModbusDeviceConfigRespDTO>> getModbusDeviceConfigList(IotModbusDeviceConfigListReqDTO listReqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -34,8 +34,12 @@ public class IotDeviceRespDTO {
|
||||
*/
|
||||
private Long productId;
|
||||
/**
|
||||
* 编解码器类型
|
||||
* 协议类型
|
||||
*/
|
||||
private String codecType;
|
||||
private String protocolType;
|
||||
/**
|
||||
* 序列化类型
|
||||
*/
|
||||
private String serializeType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.iot.core.biz.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* IoT Modbus 设备配置列表查询 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class IotModbusDeviceConfigListReqDTO {
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 模式
|
||||
*/
|
||||
private Integer mode;
|
||||
|
||||
/**
|
||||
* 协议类型
|
||||
*/
|
||||
private String protocolType;
|
||||
|
||||
/**
|
||||
* 设备 ID 集合
|
||||
*/
|
||||
private Set<Long> deviceIds;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package cn.iocoder.yudao.module.iot.core.biz.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IoT Modbus 设备配置 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class IotModbusDeviceConfigRespDTO {
|
||||
|
||||
/**
|
||||
* 设备编号
|
||||
*/
|
||||
private Long deviceId;
|
||||
/**
|
||||
* 产品标识
|
||||
*/
|
||||
private String productKey;
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
// ========== Modbus 连接配置 ==========
|
||||
|
||||
/**
|
||||
* Modbus 服务器 IP 地址
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* Modbus 服务器端口
|
||||
*/
|
||||
private Integer port;
|
||||
/**
|
||||
* 从站地址
|
||||
*/
|
||||
private Integer slaveId;
|
||||
/**
|
||||
* 连接超时时间,单位:毫秒
|
||||
*/
|
||||
private Integer timeout;
|
||||
/**
|
||||
* 重试间隔,单位:毫秒
|
||||
*/
|
||||
private Integer retryInterval;
|
||||
/**
|
||||
* 模式
|
||||
*/
|
||||
private Integer mode;
|
||||
/**
|
||||
* 数据帧格式
|
||||
*/
|
||||
private Integer frameFormat;
|
||||
|
||||
// ========== Modbus 点位配置 ==========
|
||||
|
||||
/**
|
||||
* 点位列表
|
||||
*/
|
||||
private List<IotModbusPointRespDTO> points;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package cn.iocoder.yudao.module.iot.core.biz.dto;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusByteOrderEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.modbus.IotModbusRawDataTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* IoT Modbus 点位配置 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class IotModbusPointRespDTO {
|
||||
|
||||
/**
|
||||
* 点位编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 属性标识符(物模型的 identifier)
|
||||
*/
|
||||
private String identifier;
|
||||
/**
|
||||
* 属性名称(物模型的 name)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
// ========== Modbus 协议配置 ==========
|
||||
|
||||
/**
|
||||
* Modbus 功能码
|
||||
*
|
||||
* 取值范围:FC01-04(读线圈、读离散输入、读保持寄存器、读输入寄存器)
|
||||
*/
|
||||
private Integer functionCode;
|
||||
/**
|
||||
* 寄存器起始地址
|
||||
*/
|
||||
private Integer registerAddress;
|
||||
/**
|
||||
* 寄存器数量
|
||||
*/
|
||||
private Integer registerCount;
|
||||
/**
|
||||
* 字节序
|
||||
*
|
||||
* 枚举 {@link IotModbusByteOrderEnum}
|
||||
*/
|
||||
private String byteOrder;
|
||||
/**
|
||||
* 原始数据类型
|
||||
*
|
||||
* 枚举 {@link IotModbusRawDataTypeEnum}
|
||||
*/
|
||||
private String rawDataType;
|
||||
/**
|
||||
* 缩放因子
|
||||
*/
|
||||
private BigDecimal scale;
|
||||
/**
|
||||
* 轮询间隔(毫秒)
|
||||
*/
|
||||
private Integer pollInterval;
|
||||
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {
|
||||
// ========== OTA 固件 ==========
|
||||
// 可参考:https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates
|
||||
|
||||
OTA_UPGRADE("thing.ota.upgrade", "OTA 固定信息推送", false),
|
||||
OTA_UPGRADE("thing.ota.upgrade", "OTA 固件信息推送", false),
|
||||
OTA_PROGRESS("thing.ota.progress", "OTA 升级进度上报", true),
|
||||
|
||||
;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT 协议类型枚举
|
||||
*
|
||||
* 用于定义传输层协议类型
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum IotProtocolTypeEnum implements ArrayValuable<String> {
|
||||
|
||||
TCP("tcp"),
|
||||
UDP("udp"),
|
||||
WEBSOCKET("websocket"),
|
||||
HTTP("http"),
|
||||
MQTT("mqtt"),
|
||||
EMQX("emqx"),
|
||||
COAP("coap"),
|
||||
MODBUS_TCP_CLIENT("modbus_tcp_client"),
|
||||
MODBUS_TCP_SERVER("modbus_tcp_server");
|
||||
|
||||
public static final String[] ARRAYS = Arrays.stream(values()).map(IotProtocolTypeEnum::getType).toArray(String[]::new);
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
@Override
|
||||
public String[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static IotProtocolTypeEnum of(String type) {
|
||||
return ArrayUtil.firstMatch(e -> e.getType().equals(type), values());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT 序列化类型枚举
|
||||
*
|
||||
* 用于定义设备消息的序列化格式
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum IotSerializeTypeEnum implements ArrayValuable<String> {
|
||||
|
||||
JSON("json"),
|
||||
BINARY("binary");
|
||||
|
||||
public static final String[] ARRAYS = Arrays.stream(values()).map(IotSerializeTypeEnum::getType).toArray(String[]::new);
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
@Override
|
||||
public String[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static IotSerializeTypeEnum of(String type) {
|
||||
return ArrayUtil.firstMatch(e -> e.getType().equals(type), values());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums;
|
||||
package cn.iocoder.yudao.module.iot.core.enums.device;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums.modbus;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT Modbus 字节序枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum IotModbusByteOrderEnum implements ArrayValuable<String> {
|
||||
|
||||
AB("AB", "大端序(16位)", 2),
|
||||
BA("BA", "小端序(16位)", 2),
|
||||
ABCD("ABCD", "大端序(32位)", 4),
|
||||
CDAB("CDAB", "大端字交换(32位)", 4),
|
||||
DCBA("DCBA", "小端序(32位)", 4),
|
||||
BADC("BADC", "小端字交换(32位)", 4);
|
||||
|
||||
public static final String[] ARRAYS = Arrays.stream(values())
|
||||
.map(IotModbusByteOrderEnum::getOrder)
|
||||
.toArray(String[]::new);
|
||||
|
||||
/**
|
||||
* 字节序
|
||||
*/
|
||||
private final String order;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 字节数
|
||||
*/
|
||||
private final Integer byteCount;
|
||||
|
||||
@Override
|
||||
public String[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static IotModbusByteOrderEnum getByOrder(String order) {
|
||||
return Arrays.stream(values())
|
||||
.filter(e -> e.getOrder().equals(order))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums.modbus;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT Modbus 数据帧格式枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum IotModbusFrameFormatEnum implements ArrayValuable<Integer> {
|
||||
|
||||
MODBUS_TCP(1),
|
||||
MODBUS_RTU(2);
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values())
|
||||
.map(IotModbusFrameFormatEnum::getFormat)
|
||||
.toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 格式
|
||||
*/
|
||||
private final Integer format;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums.modbus;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT Modbus 工作模式枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum IotModbusModeEnum implements ArrayValuable<Integer> {
|
||||
|
||||
POLLING(1, "云端轮询"),
|
||||
ACTIVE_REPORT(2, "边缘采集");
|
||||
|
||||
public static final Integer[] ARRAYS = Arrays.stream(values())
|
||||
.map(IotModbusModeEnum::getMode)
|
||||
.toArray(Integer[]::new);
|
||||
|
||||
/**
|
||||
* 工作模式
|
||||
*/
|
||||
private final Integer mode;
|
||||
/**
|
||||
* 模式名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public Integer[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.yudao.module.iot.core.enums.modbus;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* IoT Modbus 原始数据类型枚举
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum IotModbusRawDataTypeEnum implements ArrayValuable<String> {
|
||||
|
||||
INT16("INT16", "有符号 16 位整数", 1),
|
||||
UINT16("UINT16", "无符号 16 位整数", 1),
|
||||
INT32("INT32", "有符号 32 位整数", 2),
|
||||
UINT32("UINT32", "无符号 32 位整数", 2),
|
||||
FLOAT("FLOAT", "32 位浮点数", 2),
|
||||
DOUBLE("DOUBLE", "64 位浮点数", 4),
|
||||
BOOLEAN("BOOLEAN", "布尔值(用于线圈)", 1),
|
||||
STRING("STRING", "字符串", null); // null 表示可变长度
|
||||
|
||||
public static final String[] ARRAYS = Arrays.stream(values())
|
||||
.map(IotModbusRawDataTypeEnum::getType)
|
||||
.toArray(String[]::new);
|
||||
|
||||
/**
|
||||
* 数据类型
|
||||
*/
|
||||
private final String type;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 寄存器数量(null 表示可变)
|
||||
*/
|
||||
private final Integer registerCount;
|
||||
|
||||
@Override
|
||||
public String[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
public static IotModbusRawDataTypeEnum getByType(String type) {
|
||||
return Arrays.stream(values())
|
||||
.filter(e -> e.getType().equals(type))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
package cn.iocoder.yudao.module.iot.core.messagebus.config;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* IoT 消息总线配置属性
|
||||
*
|
||||
|
||||
@@ -24,4 +24,14 @@ public interface IotMessageBus {
|
||||
*/
|
||||
void register(IotMessageSubscriber<?> subscriber);
|
||||
|
||||
/**
|
||||
* 取消注册消息订阅者
|
||||
*
|
||||
* @param subscriber 订阅者
|
||||
*/
|
||||
default void unregister(IotMessageSubscriber<?> subscriber) {
|
||||
// TODO 芋艿:暂时不实现,需求量不大,但是
|
||||
// throw new UnsupportedOperationException("取消注册消息订阅者功能,尚未实现");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,4 +26,16 @@ public interface IotMessageSubscriber<T> {
|
||||
*/
|
||||
void onMessage(T message);
|
||||
|
||||
/**
|
||||
* 启动订阅
|
||||
*/
|
||||
default void start() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止订阅
|
||||
*/
|
||||
default void stop() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package cn.iocoder.yudao.module.iot.core.mq.message;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.device.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.state.IotDeviceStateUpdateReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@@ -60,7 +60,7 @@ public class IotDeviceMessage {
|
||||
*/
|
||||
private String serverId;
|
||||
|
||||
// ========== codec(编解码)字段 ==========
|
||||
// ========== serialize(序列化)相关字段 ==========
|
||||
|
||||
/**
|
||||
* 请求编号
|
||||
@@ -72,7 +72,7 @@ public class IotDeviceMessage {
|
||||
* 请求方法
|
||||
*
|
||||
* 枚举 {@link IotDeviceMessageMethodEnum}
|
||||
* 例如说:thing.property.report 属性上报
|
||||
* 例如说:thing.property.post 属性上报
|
||||
*/
|
||||
private String method;
|
||||
/**
|
||||
@@ -94,7 +94,7 @@ public class IotDeviceMessage {
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
// ========== 基础方法:只传递"codec(编解码)字段" ==========
|
||||
// ========== 基础方法:只传递"serialize(序列化)相关字段" ==========
|
||||
|
||||
public static IotDeviceMessage requestOf(String method) {
|
||||
return requestOf(null, method, null);
|
||||
@@ -108,6 +108,23 @@ public class IotDeviceMessage {
|
||||
return of(requestId, method, params, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建设备请求消息(包含设备信息)
|
||||
*
|
||||
* @param deviceId 设备编号
|
||||
* @param tenantId 租户编号
|
||||
* @param serverId 服务标识
|
||||
* @param method 消息方法
|
||||
* @param params 消息参数
|
||||
* @return 消息对象
|
||||
*/
|
||||
public static IotDeviceMessage requestOf(Long deviceId, Long tenantId, String serverId,
|
||||
String method, Object params) {
|
||||
IotDeviceMessage message = of(null, method, params, null, null, null);
|
||||
return message.setId(IotDeviceMessageUtils.generateMessageId())
|
||||
.setDeviceId(deviceId).setTenantId(tenantId).setServerId(serverId);
|
||||
}
|
||||
|
||||
public static IotDeviceMessage replyOf(String requestId, String method,
|
||||
Object data, Integer code, String msg) {
|
||||
if (code == null) {
|
||||
@@ -132,20 +149,12 @@ public class IotDeviceMessage {
|
||||
|
||||
public static IotDeviceMessage buildStateUpdateOnline() {
|
||||
return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(),
|
||||
MapUtil.of("state", IotDeviceStateEnum.ONLINE.getState()));
|
||||
new IotDeviceStateUpdateReqDTO(IotDeviceStateEnum.ONLINE.getState()));
|
||||
}
|
||||
|
||||
public static IotDeviceMessage buildStateOffline() {
|
||||
return requestOf(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod(),
|
||||
MapUtil.of("state", IotDeviceStateEnum.OFFLINE.getState()));
|
||||
}
|
||||
|
||||
public static IotDeviceMessage buildOtaUpgrade(String version, String fileUrl, Long fileSize,
|
||||
String fileDigestAlgorithm, String fileDigestValue) {
|
||||
return requestOf(IotDeviceMessageMethodEnum.OTA_UPGRADE.getMethod(), MapUtil.builder()
|
||||
.put("version", version).put("fileUrl", fileUrl).put("fileSize", fileSize)
|
||||
.put("fileDigestAlgorithm", fileDigestAlgorithm).put("fileDigestValue", fileDigestValue)
|
||||
.build());
|
||||
new IotDeviceStateUpdateReqDTO(IotDeviceStateEnum.OFFLINE.getState()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IoT 设备动态注册 Request DTO
|
||||
* <p>
|
||||
* 用于直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 消息的 params 参数
|
||||
* <p>
|
||||
* 直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
|
||||
@@ -27,9 +30,11 @@ public class IotDeviceRegisterReqDTO {
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 产品密钥
|
||||
* 注册签名
|
||||
*
|
||||
* @see cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils#buildSign(String, String, String)
|
||||
*/
|
||||
@NotEmpty(message = "产品密钥不能为空")
|
||||
private String productSecret;
|
||||
@NotEmpty(message = "签名不能为空")
|
||||
private String sign;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -7,7 +8,7 @@ import lombok.NoArgsConstructor;
|
||||
/**
|
||||
* IoT 设备动态注册 Response DTO
|
||||
* <p>
|
||||
* 用于直连设备/网关的一型一密动态注册响应
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#DEVICE_REGISTER} 响应的设备信息
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IoT 子设备动态注册 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.auth.register.sub 消息的 params 数组元素
|
||||
*
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 消息的 params 数组元素
|
||||
* <p>
|
||||
* 特殊:网关子设备的动态注册,必须已经创建好该网关子设备(不然哪来的 {@link #deviceName} 字段)。更多的好处,是设备不用提前烧录 deviceSecret 密钥。
|
||||
*
|
||||
* @author 芋道源码
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -7,7 +8,7 @@ import lombok.NoArgsConstructor;
|
||||
/**
|
||||
* IoT 子设备动态注册 Response DTO
|
||||
* <p>
|
||||
* 用于 thing.auth.register.sub 响应的设备信息
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#SUB_DEVICE_REGISTER} 响应的设备信息
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/register-devices">阿里云 - 动态注册子设备</a>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.config;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* IoT 设备配置推送 Request DTO
|
||||
* <p>
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#CONFIG_PUSH} 下行消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1">阿里云 - 远程配置</a>
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceConfigPushReqDTO {
|
||||
|
||||
/**
|
||||
* 配置编号
|
||||
*/
|
||||
private String configId;
|
||||
|
||||
/**
|
||||
* 配置文件大小(字节)
|
||||
*/
|
||||
private Long configSize;
|
||||
|
||||
/**
|
||||
* 签名方法
|
||||
*/
|
||||
private String signMethod;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 配置文件下载地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 获取类型
|
||||
* <p>
|
||||
* file: 文件
|
||||
* content: 内容
|
||||
*/
|
||||
private String getType;
|
||||
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.event;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IoT 设备事件上报 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.event.post 消息的 params 参数
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#EVENT_POST} 消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-events">阿里云 - 设备上报事件</a>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.ota;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* IoT 设备 OTA 升级进度上报 Request DTO
|
||||
* <p>
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#OTA_PROGRESS} 上行消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates">阿里云 - OTA 升级</a>
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceOtaProgressReqDTO {
|
||||
|
||||
/**
|
||||
* 固件版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 升级状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 升级进度(0-100)
|
||||
*/
|
||||
private Integer progress;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.ota;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* IoT 设备 OTA 固件升级推送 Request DTO
|
||||
* <p>
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#OTA_UPGRADE} 下行消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/perform-ota-updates">阿里云 - OTA 升级</a>
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceOtaUpgradeReqDTO {
|
||||
|
||||
/**
|
||||
* 固件版本号
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 固件文件下载地址
|
||||
*/
|
||||
private String fileUrl;
|
||||
|
||||
/**
|
||||
* 固件文件大小(字节)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 固件文件摘要算法
|
||||
*/
|
||||
private String fileDigestAlgorithm;
|
||||
|
||||
/**
|
||||
* 固件文件摘要值
|
||||
*/
|
||||
private String fileDigestValue;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.property;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -9,7 +10,7 @@ import java.util.Map;
|
||||
/**
|
||||
* IoT 设备属性批量上报 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.event.property.pack.post 消息的 params 参数
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_PACK_POST} 消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="http://help.aliyun.com/zh/marketplace/gateway-reports-data-in-batches">阿里云 - 网关批量上报数据</a>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.property;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* IoT 设备属性上报 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.property.post 消息的 params 参数
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_POST} 消息的 params 参数
|
||||
* <p>
|
||||
* 本质是一个 Map,key 为属性标识符,value 为属性值
|
||||
*
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.property;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* IoT 设备属性设置 Request DTO
|
||||
* <p>
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#PROPERTY_SET} 下行消息的 params 参数
|
||||
* <p>
|
||||
* 本质是一个 Map,key 为属性标识符,value 为属性值
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class IotDevicePropertySetReqDTO extends HashMap<String, Object> {
|
||||
|
||||
public IotDevicePropertySetReqDTO() {
|
||||
super();
|
||||
}
|
||||
|
||||
public IotDevicePropertySetReqDTO(Map<String, Object> properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建属性设置 DTO
|
||||
*
|
||||
* @param properties 属性数据
|
||||
* @return DTO 对象
|
||||
*/
|
||||
public static IotDevicePropertySetReqDTO of(Map<String, Object> properties) {
|
||||
return new IotDevicePropertySetReqDTO(properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.service;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* IoT 设备服务调用 Request DTO
|
||||
* <p>
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#SERVICE_INVOKE} 下行消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceServiceInvokeReqDTO {
|
||||
|
||||
/**
|
||||
* 服务标识符
|
||||
*/
|
||||
private String identifier;
|
||||
|
||||
/**
|
||||
* 服务输入参数
|
||||
*/
|
||||
private Map<String, Object> inputParams;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.state;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* IoT 设备状态更新 Request DTO
|
||||
* <p>
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#STATE_UPDATE} 消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IotDeviceStateUpdateReqDTO {
|
||||
|
||||
/**
|
||||
* 设备状态
|
||||
*/
|
||||
private Integer state;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.topo;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -9,7 +10,7 @@ import java.util.List;
|
||||
/**
|
||||
* IoT 设备拓扑添加 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.topo.add 消息的 params 参数
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#TOPO_ADD} 消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="http://help.aliyun.com/zh/marketplace/add-topological-relationship">阿里云 - 添加拓扑关系</a>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.topo;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@@ -10,7 +11,7 @@ import java.util.List;
|
||||
/**
|
||||
* IoT 设备拓扑关系变更通知 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.topo.change 下行消息的 params 参数
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#TOPO_CHANGE} 下行消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/marketplace/notify-gateway-topology-changes">阿里云 - 通知网关拓扑关系变化</a>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.topo;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
@@ -10,7 +11,7 @@ import java.util.List;
|
||||
/**
|
||||
* IoT 设备拓扑删除 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.topo.delete 消息的 params 参数
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#TOPO_DELETE} 消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/marketplace/delete-a-topological-relationship">阿里云 - 删除拓扑关系</a>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.topo;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IoT 设备拓扑关系获取 Request DTO
|
||||
* <p>
|
||||
* 用于 thing.topo.get 请求的 params 参数(目前为空,预留扩展)
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 请求的 params 参数(目前为空,预留扩展)
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.iot.core.topic.topo;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -8,7 +9,7 @@ import java.util.List;
|
||||
/**
|
||||
* IoT 设备拓扑关系获取 Response DTO
|
||||
* <p>
|
||||
* 用于 thing.topo.get 响应
|
||||
* 用于 {@link IotDeviceMessageMethodEnum#TOPO_GET} 响应
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/marketplace/obtain-topological-relationship">阿里云 - 获取拓扑关系</a>
|
||||
|
||||
@@ -25,6 +25,14 @@ public class IotDeviceAuthUtils {
|
||||
return String.format("%s.%s", productKey, deviceName);
|
||||
}
|
||||
|
||||
public static String buildClientIdFromUsername(String username) {
|
||||
IotDeviceIdentity identity = parseUsername(username);
|
||||
if (identity == null) {
|
||||
return null;
|
||||
}
|
||||
return buildClientId(identity.getProductKey(), identity.getDeviceName());
|
||||
}
|
||||
|
||||
public static String buildUsername(String productKey, String deviceName) {
|
||||
return String.format("%s&%s", deviceName, productKey);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package cn.iocoder.yudao.module.iot.core.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
|
||||
/**
|
||||
* IoT 产品【动态注册】认证工具类
|
||||
* <p>
|
||||
* 用于一型一密场景,使用 productSecret 生成签名
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class IotProductAuthUtils {
|
||||
|
||||
/**
|
||||
* 生成设备动态注册签名
|
||||
*
|
||||
* @param productKey 产品标识
|
||||
* @param deviceName 设备名称
|
||||
* @param productSecret 产品密钥
|
||||
* @return 签名
|
||||
*/
|
||||
public static String buildSign(String productKey, String deviceName, String productSecret) {
|
||||
String content = buildContent(productKey, deviceName);
|
||||
return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(productSecret))
|
||||
.digestHex(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证设备动态注册签名
|
||||
*
|
||||
* @param productKey 产品标识
|
||||
* @param deviceName 设备名称
|
||||
* @param productSecret 产品密钥
|
||||
* @param sign 待验证的签名
|
||||
* @return 是否验证通过
|
||||
*/
|
||||
public static boolean verifySign(String productKey, String deviceName, String productSecret, String sign) {
|
||||
String expectedSign = buildSign(productKey, deviceName, productSecret);
|
||||
return expectedSign.equals(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建签名内容
|
||||
*
|
||||
* @param productKey 产品标识
|
||||
* @param deviceName 设备名称
|
||||
* @return 签名内容
|
||||
*/
|
||||
private static String buildContent(String productKey, String deviceName) {
|
||||
return "deviceName" + deviceName + "productKey" + productKey;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user