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..8c412b939f 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处理异常 */ @@ -152,28 +166,51 @@ public class RecipeDeviceRecordController { // ========== 第一步:查询配方关联的点位属性信息 ========== // 1.1 根据recipeId查询iot_recipe_device_attribute表记录 - recipePlanDetailDO.setRecipeId(32L); + recipePlanDetailDO.setRecipeId(recipeId); List attributeList = recipeDeviceAttributeService.getByRecipeId(recipePlanDetailDO.getRecipeId()); -// if (CollectionUtils.isEmpty(attributeList)) { -// return success(false); // 无关联属性,直接返回 -// } - Map> deviceDataMap = deviceService.createDeviceDataMap(103L);//recipeRespVO.getDeviceId() - - for (RecipeDeviceAttributeDO attributeDO : attributeList) { - Map data = deviceDataMap.get(attributeDO.getAttributeId()); - if (data != null ) { - // 创建 - 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()); - } - recipeDeviceRecordService.createRecipeDeviceRecord(BeanUtils.toBean(recipeDeviceRecordDO, RecipeDeviceRecordSaveReqVO.class)); + //先删除在添加 + 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); + } + + 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(deviceContactModelDO.getAttributeName()); + recipeDeviceRecordDO.setDataType(deviceContactModelDO.getDataType()); + recipeDeviceRecordDO.setDataUnit(deviceContactModelDO.getDataUnit()); + recipeDeviceRecordDO.setValue((String) OpcUtils.readValue(deviceContactModelDO.getAddress())); + + recipeDeviceRecordService.createRecipeDeviceRecord(BeanUtils.toBean(recipeDeviceRecordDO, RecipeDeviceRecordSaveReqVO.class)); + } 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) { // 校验存在