【代码优化】IoT:设备下行实现前,相关代码的整理

This commit is contained in:
YunaiV 2025-01-30 07:48:28 +08:00
parent 911c8c7461
commit 5110948db8
25 changed files with 44 additions and 392 deletions

View File

@ -33,17 +33,13 @@ public enum IotPluginDeployTypeEnum implements ArrayValuable<Integer> {
this.name = name;
}
public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) {
public static IotPluginDeployTypeEnum valueOf(Integer deployType) {
return Arrays.stream(values())
.filter(value -> value.getDeployType().equals(deployType))
.findFirst()
.orElse(null);
}
public static boolean isValidDeployType(Integer deployType) {
return fromDeployType(deployType) != null;
}
@Override
public Integer[] array() {
return ARRAYS;

View File

@ -3,11 +3,11 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugin;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoImportReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoImportReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoRespVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO")
@Data
public class PluginInstanceSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
private Long id;
@Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
@NotEmpty(message = "插件主程序id不能为空")
private String mainId;
@Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
@NotNull(message = "插件id不能为空")
private Long pluginId;
@Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "插件主程序所在ip不能为空")
private String ip;
@Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "插件主程序端口不能为空")
private Integer port;
@Schema(description = "心跳时间心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "心跳时间心路时间超过30秒需要剔除不能为空")
private Long heartbeatAt;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.instance;
import lombok.*;
import io.swagger.v3.oas.annotations.media.Schema;
@ -8,11 +8,8 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @haohao建议搞个 plugin然后里面分 info instance另外是不是 info => config 会好点插件配置
@Schema(description = "管理后台 - IoT 插件实例分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class PluginInstancePageReqVO extends PageParam {
@Schema(description = "插件主程序id", example = "23738")

View File

@ -1,9 +1,11 @@
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.instance;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import lombok.Data;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - IoT 插件实例 Response VO")
@Data

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo;
package cn.iocoder.yudao.module.iot.dal.dataobject.plugin;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum;
@ -47,6 +47,7 @@ public class PluginInfoDO extends BaseDO {
* 枚举 {@link IotPluginDeployTypeEnum}
*/
private Integer deployType;
// TODO @芋艿如果是外置的插件fileName version 的选择~
/**
* 插件包文件名
*/
@ -55,6 +56,7 @@ public class PluginInfoDO extends BaseDO {
* 插件版本
*/
private String version;
// TODO @芋艿type 字典的定义
/**
* 插件类型
* <p>
@ -64,6 +66,7 @@ public class PluginInfoDO extends BaseDO {
/**
* 设备插件协议类型
*/
// TODO @芋艿枚举字段
private String protocol;
/**
* 状态
@ -71,6 +74,7 @@ public class PluginInfoDO extends BaseDO {
* 枚举 {@link IotPluginStatusEnum}
*/
private Integer status;
// TODO @芋艿configSchemaconfig 示例字段
/**
* 插件配置项描述信息
*/
@ -79,6 +83,7 @@ public class PluginInfoDO extends BaseDO {
* 插件配置信息
*/
private String config;
// TODO @芋艿script 后续的使用
/**
* 插件脚本
*/

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance;
package cn.iocoder.yudao.module.iot.dal.dataobject.plugin;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -28,6 +27,7 @@ public class PluginInstanceDO extends BaseDO {
*/
@TableId
private Long id;
// TODO @芋艿找一个更好的字段名
/**
* 插件主程序 ID
*/
@ -43,10 +43,12 @@ public class PluginInstanceDO extends BaseDO {
* 插件主程序所在 IP
*/
private String ip;
// TODO @芋艿这个 port 是否有必要记录
/**
* 插件主程序端口
*/
private Integer port;
// TODO @芋艿downstreamPort 增加目的用于下行
// TODO @haohao字段改成 heartbeatTimeLocalDateTime
/**

View File

@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.iot.dal.mysql.plugin;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.*;
/**
* IoT 插件信息 Mapper

View File

@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.dal.mysql.plugin;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.instance.PluginInstancePageReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInstanceDO;
import org.apache.ibatis.annotations.Mapper;
/**

View File

@ -1,54 +0,0 @@
package cn.iocoder.yudao.module.iot.emq.callback;
import cn.iocoder.yudao.module.iot.emq.client.EmqxClient;
import cn.iocoder.yudao.module.iot.emq.service.EmqxService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
// TODO @芋艿详细再瞅瞅
/**
* 用于处理MQTT连接的回调如连接断开消息到达消息发布完成连接完成等事件
*
* @author ahh
*/
@Slf4j
//@Component
public class EmqxCallback implements MqttCallbackExtended {
@Lazy
@Resource
private EmqxService emqxService;
@Lazy
@Resource
private EmqxClient emqxClient;
@Override
public void connectionLost(Throwable throwable) {
log.info("MQTT 连接断开", throwable);
}
@Override
public void messageArrived(String topic, MqttMessage mqttMessage) {
emqxService.subscribeCallback(topic, mqttMessage);
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
log.info("消息发送成功: {}", iMqttDeliveryToken.getMessageId());
}
@Override
public void connectComplete(boolean reconnect, String serverURI) {
log.info("MQTT 已连接到服务器: {}", serverURI);
emqxService.subscribe(emqxClient.getMqttClient());
}
}

View File

@ -1,86 +0,0 @@
package cn.iocoder.yudao.module.iot.emq.client;
import cn.iocoder.yudao.module.iot.emq.callback.EmqxCallback;
import cn.iocoder.yudao.module.iot.emq.config.MqttConfig;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.stereotype.Component;
/**
* MQTT客户端类负责建立与MQTT服务器的连接提供发布消息和订阅主题的功能
*
* @author ahh
*/
@Slf4j
@Data
//@Component
public class EmqxClient {
@Resource
private EmqxCallback emqxCallback;
@Resource
private MqttConfig mqttConfig;
private MqttClient mqttClient;
public void connect() {
if (mqttClient == null) {
createMqttClient();
}
try {
mqttClient.connect(createMqttOptions());
log.info("MQTT客户端连接成功");
} catch (MqttException e) {
log.error("MQTT客户端连接失败", e);
}
}
private void createMqttClient() {
try {
mqttClient = new MqttClient(mqttConfig.getHostUrl(), "yudao" + mqttConfig.getClientId(), new MemoryPersistence());
mqttClient.setCallback(emqxCallback);
} catch (MqttException e) {
log.error("创建MQTT客户端失败", e);
}
}
private MqttConnectOptions createMqttOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(mqttConfig.getUsername());
options.setPassword(mqttConfig.getPassword().toCharArray());
options.setConnectionTimeout(mqttConfig.getTimeout());
options.setKeepAliveInterval(mqttConfig.getKeepalive());
options.setCleanSession(mqttConfig.isClearSession());
return options;
}
public void publish(String topic, String message) {
try {
if (mqttClient == null || !mqttClient.isConnected()) {
connect();
}
mqttClient.publish(topic, new MqttMessage(message.getBytes()));
log.info("消息已发布到主题: {}", topic);
} catch (MqttException e) {
log.error("消息发布失败", e);
}
}
public void subscribe(String topic) {
try {
if (mqttClient == null || !mqttClient.isConnected()) {
connect();
}
mqttClient.subscribe(topic);
log.info("订阅了主题: {}", topic);
} catch (MqttException e) {
log.error("订阅主题失败", e);
}
}
}

View File

@ -1,68 +0,0 @@
package cn.iocoder.yudao.module.iot.emq.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
// TODO @芋艿详细再瞅瞅
/**
* 配置类用于读取MQTT连接的配置信息如用户名密码连接地址等
*
* @author ahh
*/
@Data
//@Component
//@ConfigurationProperties("iot.emq")
public class MqttConfig {
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 连接地址
*/
private String hostUrl;
/**
* 客户Id
*/
private String clientId;
/**
* 默认连接话题
*/
private String defaultTopic;
/**
* 超时时间
*/
private int timeout;
/**
* 保持连接数
*/
private int keepalive;
/**
* 是否清除session
*/
private boolean clearSession;
/**
* 是否共享订阅
*/
private boolean isShared;
/**
* 分组共享订阅
*/
private boolean isSharedGroup;
}

View File

@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.iot.emq.service;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
// TODO @芋艿在瞅瞅
/**
* 用于处理MQTT消息的具体业务逻辑如订阅回调
*
* @author ahh
*/
public interface EmqxService {
/**
* 订阅回调
*
* @param topic 主题
* @param mqttMessage 消息
*/
void subscribeCallback(String topic, MqttMessage mqttMessage);
/**
* 订阅主题
*
* @param client MQTT 客户端
*/
void subscribe(MqttClient client);
}

View File

@ -1,56 +0,0 @@
package cn.iocoder.yudao.module.iot.emq.service;
import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.scheduling.annotation.Async;
// TODO @芋艿在瞅瞅
/**
* 用于处理MQTT消息的具体业务逻辑如订阅回调
*
* @author ahh
*/
@Slf4j
// @Service
public class EmqxServiceImpl implements EmqxService {
@Resource
private IotDevicePropertyService iotDeviceDataService;
// TODO 多线程处理消息
@Override
@Async
public void subscribeCallback(String topic, MqttMessage mqttMessage) {
log.info("收到消息,主题: {}, 内容: {}", topic, new String(mqttMessage.getPayload()));
// 根据不同的主题处理不同的业务逻辑
if (topic.contains("/property/post")) {
// 设备上报数据 topic /sys/f13f57c63e9/dianbiao1/thing/event/property/post
// TODO @hao这块未来可能搞个 IotTopicUrls 之类把拼接和解析的逻辑收敛
String productKey = topic.split("/")[2];
String deviceName = topic.split("/")[3];
String message = new String(mqttMessage.getPayload());
// IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder()
// .productKey(productKey)
// .deviceName(deviceName)
// .properties(message) // TODO 芋艿临时去掉看看
// .build();
// iotDeviceDataService.saveDeviceProperty(createDTO);
}
}
@Override
public void subscribe(MqttClient client) {
try {
// 订阅默认主题可以根据需要修改
client.subscribe("/sys/+/+/#", 1);
log.info("订阅默认主题成功");
} catch (Exception e) {
log.error("订阅默认主题失败", e);
}
}
}

View File

@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.iot.emq.start;
import cn.iocoder.yudao.module.iot.emq.client.EmqxClient;
import jakarta.annotation.Resource;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
// TODO @芋艿在瞅瞅
/**
* 用于在应用启动时自动连接MQTT服务器
*
* @author ahh
*/
//@Component
public class EmqxStart implements ApplicationRunner {
@Resource
private EmqxClient emqxClient;
@Override
public void run(ApplicationArguments applicationArguments) {
emqxClient.connect();
}
}

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.framework.plugin;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService;
import lombok.extern.slf4j.Slf4j;

View File

@ -18,7 +18,6 @@ public class UnifiedConfiguration {
private String pluginsDir;
@Bean
// @DependsOn("deviceDataApiImpl")
public SpringPluginManager pluginManager() {
log.info("[init][实例化 SpringPluginManager]");
SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) {

View File

@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.iot.service.plugin;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
import jakarta.validation.Valid;
import org.springframework.web.multipart.MultipartFile;

View File

@ -2,9 +2,9 @@ package cn.iocoder.yudao.module.iot.service.plugin;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoSaveReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper;
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
import jakarta.annotation.Resource;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.iot.service.plugin;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import org.springframework.web.multipart.MultipartFile;
/**
@ -10,6 +10,7 @@ import org.springframework.web.multipart.MultipartFile;
*/
public interface PluginInstanceService {
// TODO @芋艿这个是否应该放到 plugin 主动心跳而是 server 自己心跳
/**
* 上报插件实例心跳
*/

View File

@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.service.plugin;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInfoDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.PluginInstanceDO;
import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper;
import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants;
@ -45,8 +45,10 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
@Resource
private PluginInfoMapper pluginInfoMapper;
@Resource
private PluginInstanceMapper pluginInstanceMapper;
@Resource
private SpringPluginManager pluginManager;