Compare commits

...

3 Commits
main ... hhk

@ -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);

@ -52,6 +52,12 @@
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.cronutils</groupId>
<artifactId>cron-utils</artifactId>
<version>9.2.0</version>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>

@ -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<DeviceContactModelDO> 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.<DevicePointRulesDO>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<DeviceContactModelDO> 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<PointRulesRespVO> pointRulesVOList = JSON.parseArray(

@ -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);

@ -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不存在");

@ -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) {
// 校验存在

Loading…
Cancel
Save