diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/opc/OpcUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/opc/OpcUtils.java
index a9e6d816d3..ed78711033 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/opc/OpcUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/opc/OpcUtils.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.common.util.opc;
+import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder;
@@ -24,6 +25,7 @@ import java.util.function.Predicate;
* OPC UA连接静态工具类 - 适用于Eclipse Milo 0.6.9
* 提供OPC UA服务器的连接、断开、数据读写等功能(静态方法版本)
*/
+@Slf4j
public class OpcUtils {
// 静态成员变量,所有实例共享
@@ -46,14 +48,15 @@ public class OpcUtils {
public static boolean connect(String url, String username, String password, int timeoutSeconds) {
if (isConnected) {
- System.out.println(LOG_PREFIX + "客户端已连接,无需重复连接");
+ log.info(" {} 客户端已连接,无需重复连接",LOG_PREFIX);
return true;
}
serverUrl = url;
try {
- System.out.println(LOG_PREFIX + "正在连接到OPC UA服务器: " + url);
+
+ log.info(" {} 正在连接到OPC UA服务器 {}",LOG_PREFIX,url);
// 提取主机和端口
final String targetHost = extractHostFromUrl(url);
@@ -62,6 +65,7 @@ public class OpcUtils {
System.out.println(LOG_PREFIX + "目标主机: " + targetHost + ", 端口: " + targetPort + ", 路径: " + path);
+
// 将主机名解析为IP地址
final String ipAddress = resolveToIpAddress(targetHost);
System.out.println(LOG_PREFIX + "解析为IP地址: " + ipAddress);
diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml
index 60e41dee82..3284ebf3e7 100644
--- a/yudao-module-iot/yudao-module-iot-biz/pom.xml
+++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -52,6 +52,12 @@
yudao-spring-boot-starter-mybatis
+
+ com.cronutils
+ cron-utils
+ 9.2.0
+
+
cn.iocoder.boot
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java
index 4da6014705..39295bccf7 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/scheduled/coretask/DeviceTask.java
@@ -1,11 +1,14 @@
// DeviceTask.java - 原有设备任务
package cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.coretask;
+import cn.iocoder.yudao.framework.common.enums.DeviceConnectionStatusEnum;
import cn.iocoder.yudao.framework.common.util.opc.OpcUtils;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceBasicStatusEnum;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceStatusEnum;
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.TaskTypeEnum;
import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.core.Task;
+import cn.iocoder.yudao.module.iot.controller.admin.device.scheduled.scheduler.TaskSchedulerManager;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
@@ -17,6 +20,7 @@ import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactMod
import cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord.DeviceOperationRecordMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper;
import cn.iocoder.yudao.module.iot.dal.mysql.devicewarinningrecord.DeviceWarinningRecordMapper;
+import cn.iocoder.yudao.module.iot.service.device.DeviceService;
import cn.iocoder.yudao.module.iot.service.device.TDengineService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -26,6 +30,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@@ -48,6 +53,10 @@ public class DeviceTask implements Task {
@Resource
private TDengineService tDengineService;
+ @Resource
+ @Lazy
+ private DeviceService deviceService;
+
@Resource
private DevicePointRulesMapper devicePointRulesMapper;
@@ -57,6 +66,10 @@ public class DeviceTask implements Task {
@Resource
private DeviceWarinningRecordMapper deviceWarinningRecordMapper;
+ @Resource
+ @Lazy
+ private TaskSchedulerManager taskSchedulerManager;
+
@Override
public String getTaskType() {
return TaskTypeEnum.DEVICE.getCode();
@@ -103,13 +116,6 @@ public class DeviceTask implements Task {
logger.info("执行设备任务,任务ID: {}, 参数: {}, 时间: {}",
taskId, taskParam, currentTime);
- // 解析参数,假设格式为 deviceId:deviceCode
-// String[] params = taskParam.split(":");
-// if (params.length >= 2) {
-// Long deviceId = Long.parseLong(params[0]);
-// String deviceCode = params[1];
-// executeDeviceLogic(deviceId, deviceCode);
-// }
executeDeviceLogic(taskId,taskParam);
@@ -148,32 +154,6 @@ public class DeviceTask implements Task {
OpcUtils.disconnect();
}
- /**
- * 具体的设备执行逻辑
- */
-// private void executeDeviceLogic(Long sourceDeviceId, String param) {
-// logger.info("执行设备逻辑,源设备ID: {},参数: {}", sourceDeviceId, param);
-//
-// // 1. 计算实际设备ID
-// Long deviceId = sourceDeviceId - 1000000L;
-// logger.info("处理后设备ID: {}", deviceId);
-//
-// if (deviceId == null) {
-// throw new RuntimeException("设备ID不能为空");
-// }
-//
-// // 2. 获取设备信息
-// DeviceDO device = getDeviceInfo(deviceId);
-//
-// // 3. 连接OPC服务器
-// connectOpcServer(device);
-//
-// // 4. 处理数据读取和入库
-// processDeviceData(deviceId, device);
-//
-// // 5. 断开连接
-// OpcUtils.disconnect();
-// }
/**
* 获取设备信息
@@ -199,25 +179,95 @@ public class DeviceTask implements Task {
String username = StringUtils.defaultString(device.getUsername());
String password = StringUtils.defaultString(device.getPassword());
- boolean connected = OpcUtils.connect(device.getUrl(), username, password, 10);
- if (!connected) {
- throw new RuntimeException("连接OPC服务器失败,URL: " + device.getUrl());
+ boolean connected = false;
+
+
+ try {
+ connected = OpcUtils.connect(device.getUrl(), username, password, 10);
+ if (!connected) {
+
+ log.error("设备 {} 连接OPC服务器失败,URL: {}", device.getId(), device.getUrl());
+ device.setStatus(String.valueOf(DeviceConnectionStatusEnum.DISCONNECTED.getStatus()));
+ deviceMapper.updateById(device);
+ taskSchedulerManager.stopDeviceTask(device.getId());
+ DeviceOperationRecordDO deviceOperationRecordDO = new DeviceOperationRecordDO();
+ deviceOperationRecordDO.setDeviceId(device.getId());
+ deviceOperationRecordDO.setRule(DeviceStatusEnum.OFFLINE.getCode());
+ //TODO 默认内置管理员
+ deviceOperationRecordDO.setCreator("1");
+ deviceOperationRecordDO.setUpdater("1");
+ deviceOperationRecordMapper.insert(deviceOperationRecordDO);
+ //抛出异常终止任务
+ throw new RuntimeException("连接opcuv服务器异常");
+
+ }
+
+ log.info("设备 {} 成功连接OPC服务器,URL: {}", device.getId(), device.getUrl());
+
+ } catch (Exception e) {
+ log.error("设备 {} 连接OPC服务器异常,URL: {}", device.getId(), device.getUrl(), e);
+ device.setStatus(String.valueOf(DeviceConnectionStatusEnum.DISCONNECTED.getStatus()));
+ deviceMapper.updateById(device);
+ taskSchedulerManager.stopDeviceTask(device.getId());
+ DeviceOperationRecordDO deviceOperationRecordDO = new DeviceOperationRecordDO();
+ deviceOperationRecordDO.setDeviceId(device.getId());
+ deviceOperationRecordDO.setRule(DeviceStatusEnum.OFFLINE.getCode());
+ //TODO 默认内置管理员
+ deviceOperationRecordDO.setCreator("1");
+ deviceOperationRecordDO.setUpdater("1");
+ deviceOperationRecordMapper.insert(deviceOperationRecordDO);
+ //抛出异常终止任务
+ throw new RuntimeException(e);
}
+
+
}
/**
* 处理设备数据
*/
private void processDeviceData(Long deviceId, DeviceDO device) {
+ DeviceDO deviceDO = deviceMapper.selectById(deviceId);
// 1. 查询点位配置
List points = getDevicePoints(deviceId);
if (CollectionUtils.isEmpty(points)) {
+
+
+
logger.warn("设备 {} 未配置点位", deviceId);
+ //更新状态为待机中
+ DeviceOperationRecordDO record = new DeviceOperationRecordDO();
+ record.setDeviceId(device.getId());
+ record.setRule(DeviceStatusEnum.STANDBY.getCode());
+ record.setTotalStandbyTime(deviceDO.getSampleCycle());
+ //TODO 创建人和更新人为内置默认管理员
+ record.setCreator("1");
+ record.setUpdater("1");
+
+
+ deviceOperationRecordMapper.insert(record);
return;
}
logger.info("设备 {} 需要读取 {} 个点位", deviceId, points.size());
+
+ DevicePointRulesDO devicePointRulesDO = devicePointRulesMapper.selectOne(Wrappers.lambdaQuery()
+ .eq(DevicePointRulesDO::getDeviceId, deviceId)
+ .eq(DevicePointRulesDO::getIdentifier, DeviceBasicStatusEnum.RUNNING));
+ if(devicePointRulesDO !=null && devicePointRulesDO.getFieldRule() == null ){
+ //更新状态为待机中
+ DeviceOperationRecordDO record = new DeviceOperationRecordDO();
+ record.setDeviceId(device.getId());
+ record.setRule(DeviceStatusEnum.STANDBY.getCode());
+ record.setTotalStandbyTime(deviceDO.getSampleCycle());
+ //TODO 创建人和更新人为内置默认管理员
+ record.setCreator("1");
+ record.setUpdater("1");
+ deviceOperationRecordMapper.insert(record);
+ }
+
+
// 2. 读取并处理数据
int successCount = 0;
List validDataList = new ArrayList<>();
@@ -333,9 +383,9 @@ public class DeviceTask implements Task {
// 2. 遍历规则
for (DevicePointRulesDO devicePointRulesDO : devicePointRulesDOList) {
- if (StringUtils.isBlank(devicePointRulesDO.getFieldRule())) {
+ if (StringUtils.isBlank(devicePointRulesDO.getFieldRule())) {
continue;
- }
+ }
// 3. 解析规则列表
List pointRulesVOList = JSON.parseArray(
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeRespVO.java
index 80dec1c437..6bbee8c9f0 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeRespVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeRespVO.java
@@ -56,4 +56,7 @@ public class RecipeRespVO {
// @ExcelProperty("关联设备ID")
private Long deviceId;
+ @Schema(description = "设备Id")
+ private Long machineId;
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeSaveReqVO.java
index c432c6b283..3826accc3a 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeSaveReqVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipe/vo/RecipeSaveReqVO.java
@@ -37,4 +37,7 @@ public class RecipeSaveReqVO {
@Schema(description = "数据单位")
private String dataUnit;
+ @Schema(description = "设备Id")
+ private Long machineId;
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java
index a0ba78a9d4..4615134994 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java
@@ -1,9 +1,15 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipedevicerecord;
+import cn.iocoder.yudao.framework.common.util.opc.OpcUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.DeviceRespVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.recipe.RecipeDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipeplandetail.RecipePlanDetailDO;
import cn.iocoder.yudao.module.iot.service.device.DeviceService;
+import cn.iocoder.yudao.module.iot.service.devicecontactmodel.DeviceContactModelService;
import cn.iocoder.yudao.module.iot.service.recipe.RecipeService;
import cn.iocoder.yudao.module.iot.service.recipeplandetail.RecipePlanDetailService;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@@ -16,17 +22,23 @@ import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RECIPE_NOT_EXISTS;
import cn.iocoder.yudao.module.iot.controller.admin.recipedevicerecord.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipedevicerecord.RecipeDeviceRecordDO;
@@ -73,6 +85,9 @@ public class RecipeDeviceRecordController {
@Resource
private DeviceService deviceService;
+ @Resource
+ private DeviceContactModelService deviceContactModelService;
+
@PostMapping("/create")
@@ -134,7 +149,6 @@ public class RecipeDeviceRecordController {
/**
* 批量创建设备点位采集记录和配方点位记录
* @param recipeId 配方ID
- * @param pointList 点位列表 [{id: 1, refer: "参考值1"}, {id: 2, refer: "参考值2"}]
* @return 创建结果
* @throws JsonProcessingException JSON处理异常
*/
@@ -145,37 +159,65 @@ public class RecipeDeviceRecordController {
@RequestParam("id") Long recipeId) {
- RecipePlanDetailDO recipePlanDetailDO = recipePlanDetailService.getRecipePlanDetail(recipeId);
- //RecipeRespVO recipeRespVO = recipeService.getRecipeWithDeviceId(recipePlanDetailDO.getRecipeId());
+ try {
+ RecipePlanDetailDO recipePlanDetailDO = recipePlanDetailService.getRecipePlanDetail(recipeId);
+ //RecipeRespVO recipeRespVO = recipeService.getRecipeWithDeviceId(recipePlanDetailDO.getRecipeId());
+
+ //RecipeDO recipeDO = recipeService.getRecipe(recipePlanDetailDO.getRecipeId());
+ // ========== 第一步:查询配方关联的点位属性信息 ==========
+ // 1.1 根据recipeId查询iot_recipe_device_attribute表记录
- //RecipeDO recipeDO = recipeService.getRecipe(recipePlanDetailDO.getRecipeId());
- // ========== 第一步:查询配方关联的点位属性信息 ==========
- // 1.1 根据recipeId查询iot_recipe_device_attribute表记录
+ recipePlanDetailDO.setRecipeId(recipeId);
+ List attributeList = recipeDeviceAttributeService.getByRecipeId(recipePlanDetailDO.getRecipeId());
- recipePlanDetailDO.setRecipeId(32L);
- List attributeList = recipeDeviceAttributeService.getByRecipeId(recipePlanDetailDO.getRecipeId());
-// if (CollectionUtils.isEmpty(attributeList)) {
-// return success(false); // 无关联属性,直接返回
-// }
- Map> deviceDataMap = deviceService.createDeviceDataMap(103L);//recipeRespVO.getDeviceId()
+ //先删除在添加
+ List recipeDeviceRecordDOS = recipeDeviceRecordService.getListByRecipeId(recipeId);
+ if (!recipeDeviceRecordDOS.isEmpty()){
+ recipeDeviceRecordService.deleteByIds(recipeDeviceRecordDOS);
+ }
+ RecipeDO recipe = recipeService.getRecipe(recipeId);
+ if (recipe == null){
+ throw exception(RECIPE_NOT_EXISTS);
+ }
+
+ DeviceRespVO device = deviceService.getDevice(recipe.getMachineId());
+ if (device== null ){
+ throw exception(DEVICE_NOT_EXISTS);
+ }
- for (RecipeDeviceAttributeDO attributeDO : attributeList) {
- Map data = deviceDataMap.get(attributeDO.getAttributeId());
- if (data != null ) {
+ Map deviceContactModelMap = new HashMap<>();
+ List deviceContactModelDOS = deviceContactModelService.selectListByDeviceId(device.getId());
+ if (!deviceContactModelDOS.isEmpty()){
+ deviceContactModelMap = deviceContactModelDOS.stream()
+ .collect(Collectors.toMap(
+ DeviceContactModelDO::getId,
+ Function.identity()
+ ));
+ }
+
+ OpcUtils.connect(device.getUrl(),device.getUsername(),device.getPassword(),10);
+
+ for (RecipeDeviceAttributeDO attributeDO : attributeList) {
+ DeviceContactModelDO deviceContactModelDO = deviceContactModelMap.get(attributeDO.getAttributeId());
+ if (deviceContactModelDO == null){
+ continue;
+ }
// 创建
RecipeDeviceRecordDO recipeDeviceRecordDO = new RecipeDeviceRecordDO();
recipeDeviceRecordDO.setRecipeId(recipeId);
- recipeDeviceRecordDO.setAttributeCode(attributeDO.getAttributeName());
- recipeDeviceRecordDO.setDataType(attributeDO.getDataType());
- recipeDeviceRecordDO.setDataUnit(attributeDO.getDataUnit());
- if (data.get("addressValue") != null && data.get("addressValue").toString() != null) {
- recipeDeviceRecordDO.setValue(data.get("addressValue").toString());
- }
+ recipeDeviceRecordDO.setAttributeCode(deviceContactModelDO.getAttributeName());
+ recipeDeviceRecordDO.setDataType(deviceContactModelDO.getDataType());
+ recipeDeviceRecordDO.setDataUnit(deviceContactModelDO.getDataUnit());
+ recipeDeviceRecordDO.setValue((String) OpcUtils.readValue(deviceContactModelDO.getAddress()));
+
recipeDeviceRecordService.createRecipeDeviceRecord(BeanUtils.toBean(recipeDeviceRecordDO, RecipeDeviceRecordSaveReqVO.class));
}
+ } finally {
+ OpcUtils.disconnect();
}
+
return success(true);
}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordPageReqVO.java
index 3cc8aeefd6..880ce1901b 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordPageReqVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordPageReqVO.java
@@ -52,4 +52,5 @@ public class RecipeDeviceRecordPageReqVO extends PageParam {
@Schema(description = "配方id", example = "32535")
private Long recipeId;
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/RecipePlanDetailSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/RecipePlanDetailSaveReqVO.java
index 2598ec191b..4bfda9dafa 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/RecipePlanDetailSaveReqVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/RecipePlanDetailSaveReqVO.java
@@ -25,7 +25,7 @@ public class RecipePlanDetailSaveReqVO {
private Long recipeId;
@Schema(description = "关联计划(关联mes_plan表的id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "28398")
- @NotNull(message = "关联计划(关联mes_plan表的id)不能为空")
+// @NotNull(message = "关联计划(关联mes_plan表的id)不能为空")
private Long planId;
@Schema(description = "来源(新增/生产中)")
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipe/RecipeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipe/RecipeDO.java
index c3c943865a..81e54841b7 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipe/RecipeDO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipe/RecipeDO.java
@@ -60,4 +60,11 @@ public class RecipeDO extends BaseDO {
*/
private String dataUnit;
+ /**
+ * 数据单位
+ */
+ private Long machineId;
+
+
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java
index 4f15ac6780..f881fc7d6d 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java
@@ -523,9 +523,16 @@ public class DeviceServiceImpl implements DeviceService {
}else if(Objects.equals(createReqVO.getIsConnect(), DeviceConnectionStatusEnum.DISCONNECTED.getStatus())){
boolean disconnect = OpcUtils.disconnect();
if (disconnect){
+ //更新连接状态
deviceDO.setStatus(String.valueOf(DeviceConnectionStatusEnum.DISCONNECTED.getStatus()));
deviceMapper.updateById(deviceDO);
+ //停止定时任务
taskSchedulerManager.stopDeviceTask(deviceDO.getId());
+ //更新运行状态
+ updateOperationalStatus(deviceDO);
+
+
+
}else {
throw exception(OPC_CLOSE_CONNECT_FAILURE);
}
@@ -536,6 +543,13 @@ public class DeviceServiceImpl implements DeviceService {
return Boolean.TRUE;
}
+ private void updateOperationalStatus(DeviceDO deviceDO) {
+ DeviceOperationRecordDO deviceOperationRecordDO = new DeviceOperationRecordDO();
+ deviceOperationRecordDO.setDeviceId(deviceDO.getId());
+ deviceOperationRecordDO.setRule(DeviceStatusEnum.OFFLINE.getCode());
+ deviceOperationRecordMapper.insert(deviceOperationRecordDO);
+ }
+
private DeviceDO validateConnectRequest(DeviceSaveReqVO createReqVO) {
if(createReqVO.getId() == null){
throw exception(DEVICE_ID_DOES_NOT_EXIST);
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java
index 8eca7be5bc..5daff8797e 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelService.java
@@ -53,4 +53,6 @@ public interface DeviceContactModelService {
PageResult getDeviceContactModelPage(DeviceContactModelPageReqVO pageReqVO);
List getDeviceContactModelList(Long id);
+
+ List selectListByDeviceId(Long id);
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java
index b6cbb5f719..0108420cbd 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/devicecontactmodel/DeviceContactModelServiceImpl.java
@@ -99,4 +99,9 @@ public class DeviceContactModelServiceImpl implements DeviceContactModelService
.orderByDesc(DeviceContactModelDO::getCreateTime));
}
+ @Override
+ public List selectListByDeviceId(Long id) {
+ return deviceContactModelMapper.selectList(Wrappers.lambdaQuery().eq(DeviceContactModelDO::getDeviceId,id));
+ }
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordService.java
index b05b6d8371..9b4e3107a6 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordService.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordService.java
@@ -52,4 +52,7 @@ public interface RecipeDeviceRecordService {
*/
PageResult getRecipeDeviceRecordPage(RecipeDeviceRecordPageReqVO pageReqVO);
+ List getListByRecipeId(Long recipeId);
+
+ void deleteByIds(List recipeDeviceRecordDOS);
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordServiceImpl.java
index 45e7db22ba..e3ab5db969 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordServiceImpl.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipedevicerecord/RecipeDeviceRecordServiceImpl.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.service.recipedevicerecord;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@@ -31,6 +32,7 @@ public class RecipeDeviceRecordServiceImpl implements RecipeDeviceRecordService
@Override
public Long createRecipeDeviceRecord(RecipeDeviceRecordSaveReqVO createReqVO) {
+
// 插入
RecipeDeviceRecordDO recipeDeviceRecord = BeanUtils.toBean(createReqVO, RecipeDeviceRecordDO.class);
recipeDeviceRecordMapper.insert(recipeDeviceRecord);
@@ -71,4 +73,16 @@ public class RecipeDeviceRecordServiceImpl implements RecipeDeviceRecordService
return recipeDeviceRecordMapper.selectPage(pageReqVO);
}
+ @Override
+ public List getListByRecipeId(Long recipeId) {
+ return recipeDeviceRecordMapper.selectList(Wrappers.lambdaQuery().eq(RecipeDeviceRecordDO::getRecipeId, recipeId));
+
+ }
+
+ @Override
+ public void deleteByIds(List recipeDeviceRecordDOS) {
+ recipeDeviceRecordMapper.deleteByIds(recipeDeviceRecordDOS);
+
+ }
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipepointrecord/RecipePointRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipepointrecord/RecipePointRecordServiceImpl.java
index c08cb7bfc5..a07dbee010 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipepointrecord/RecipePointRecordServiceImpl.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipepointrecord/RecipePointRecordServiceImpl.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.service.recipepointrecord;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@@ -31,6 +32,12 @@ public class RecipePointRecordServiceImpl implements RecipePointRecordService {
@Override
public Long createRecipePointRecord(RecipePointRecordSaveReqVO createReqVO) {
+ List recipePointRecordDOS = recipePointRecordMapper.selectList(Wrappers.lambdaQuery().eq(RecipePointRecordDO::getRecipeId, createReqVO.getRecipeId()));
+ if (!recipePointRecordDOS.isEmpty()){
+ recipePointRecordMapper.deleteByIds(recipePointRecordDOS);
+ }
+
+
// 插入
RecipePointRecordDO recipePointRecord = BeanUtils.toBean(createReqVO, RecipePointRecordDO.class);
recipePointRecordMapper.insert(recipePointRecord);
diff --git a/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 6618f4adf1..cb6adedc8f 100644
--- a/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
+++ b/yudao-module-mes/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -143,6 +143,8 @@ public interface ErrorCodeConstants {
ErrorCode SUBJECT_EXISTS = new ErrorCode(1002000010, "项目编码已存在");
ErrorCode TASK_MANAGEMENT_NOT_EXISTS = new ErrorCode(1002000011, "设备类型不存在");
ErrorCode TASK_CORN_NOT_EXISTS = new ErrorCode(1002000011, "设备corn表达式为空");
+ ErrorCode TASK_CORN_NOT_LE_HOUR = new ErrorCode(1002000011, "corn表达式不能小于一小时");
+
ErrorCode TICKET_MANAGEMENT_NOT_EXISTS = new ErrorCode(1002000012, "工单管理不存在");
ErrorCode TICKET_RESULTS_NOT_EXISTS = new ErrorCode(1002000013, "工单检验结果不存在");
ErrorCode TICKET_RESULTS_ID_NOT_NULL = new ErrorCode(1002000014, "工单检验结果Id不存在");
diff --git a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/taskmanagement/TaskManagementServiceImpl.java b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/taskmanagement/TaskManagementServiceImpl.java
index e39cc002bc..035dd42143 100644
--- a/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/taskmanagement/TaskManagementServiceImpl.java
+++ b/yudao-module-mes/yudao-module-mes-biz/src/main/java/cn/iocoder/yudao/module/mes/service/taskmanagement/TaskManagementServiceImpl.java
@@ -16,6 +16,11 @@ import cn.iocoder.yudao.module.mes.dal.mysql.ticketmanagement.TicketManagementMa
import cn.iocoder.yudao.module.mes.dal.mysql.ticketresults.TicketResultsMapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cronutils.model.Cron;
+import com.cronutils.model.CronType;
+import com.cronutils.model.definition.CronDefinitionBuilder;
+import com.cronutils.model.time.ExecutionTime;
+import com.cronutils.parser.CronParser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -23,6 +28,8 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;
@@ -73,6 +80,12 @@ public class TaskManagementServiceImpl implements TaskManagementService {
@Override
public Long createTaskManagement(TaskManagementSaveReqVO createReqVO) {
+ String cronExpr = createReqVO.getCronExpression();
+
+ // 校验 cron 表达式最小间隔
+ isCronIntervalAtLeastOneHour(cronExpr);
+
+
// 插入
TaskManagementDO taskManagement = BeanUtils.toBean(createReqVO, TaskManagementDO.class);
taskManagementMapper.insert(taskManagement);
@@ -80,6 +93,35 @@ public class TaskManagementServiceImpl implements TaskManagementService {
return taskManagement.getId();
}
+ private void isCronIntervalAtLeastOneHour(String cronExpr) {
+
+ try {
+ if (StringUtils.isBlank(cronExpr)){
+ return;
+ }
+
+ CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ));
+ Cron cron = parser.parse(cronExpr);
+ cron.validate();
+
+ ExecutionTime executionTime = ExecutionTime.forCron(cron);
+
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next1 = executionTime.nextExecution(now).get();
+ ZonedDateTime next2 = executionTime.nextExecution(next1).get();
+
+ Duration duration = Duration.between(next1, next2);
+
+ if (duration.toHours() < 1) {
+ throw exception(TASK_CORN_NOT_LE_HOUR);
+ }
+
+ } catch (Exception e) {
+ throw exception(TASK_CORN_NOT_LE_HOUR);
+ }
+
+ }
+
@Override
public void updateTaskManagement(TaskManagementSaveReqVO updateReqVO) {
// 校验存在