Compare commits

...

11 Commits
plp ... main

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

@ -54,11 +54,11 @@ public class ErpProductController {
ErpProductPageReqVO productPageReqVO = new ErpProductPageReqVO();
productPageReqVO.setName(createReqVO.getName());
if (!productMapper.selectProductExist(productPageReqVO).getList().isEmpty()) {
return error(400,"名称不能重复");
return error(400,"名称+规格不能重复");
}
productPageReqVO = new ErpProductPageReqVO();
productPageReqVO.setCode(createReqVO.getBarCode());
if (!productMapper.selectProductExist(productPageReqVO).getList().isEmpty()) {
if (!productMapper.selectProductCodeExist(productPageReqVO).getList().isEmpty()) {
return error(400,"编码不能重复");
}
return success(productService.createProduct(createReqVO));

@ -115,4 +115,12 @@ public class ErpProductUnitController {
}
@GetMapping("/flag-list")
@Operation(summary = "获得产品主单位列表")
public CommonResult<List<ErpProductUnitDO>> getProductUnitListByFlag() {
List<ErpProductUnitDO> productUnitDOList = productUnitService.getProductUnitListByFlag();
return success(productUnitDOList);
}
}

@ -27,4 +27,7 @@ public class ErpProductPageReqVO extends PageParam {
@Schema(description = "产品编号", example = "11161")
private String code;
@Schema(description = "产品规格", example = "红色")
private String standard;
}

@ -24,4 +24,13 @@ public class ErpProductUnitSaveReqVO {
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "是否主单位", example = "芋艿")
private String primaryFlag;
@Schema(description = "关联主单位id", example = "芋艿")
private Long primaryId;
@Schema(description = "换算比例", example = "芋艿")
private Double changeRate;
}

@ -29,13 +29,20 @@ public interface ErpProductMapper extends BaseMapperX<ErpProductDO> {
.orderByDesc(ErpProductDO::getId));
}
default PageResult<ErpProductDO> selectProductExist(ErpProductPageReqVO reqVO) {
default PageResult<ErpProductDO> selectProductCodeExist(ErpProductPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ErpProductDO>()
.eqIfPresent(ErpProductDO::getName, reqVO.getName())
.eqIfPresent(ErpProductDO::getBarCode, reqVO.getCode())
.orderByAsc(ErpProductDO::getId));
}
default PageResult<ErpProductDO> selectProductExist(ErpProductPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ErpProductDO>()
.eqIfPresent(ErpProductDO::getName, reqVO.getName())
.eqIfPresent(ErpProductDO::getStandard, reqVO.getStandard())
.orderByAsc(ErpProductDO::getId));
}
default Long selectCountByCategoryId(Long categoryId) {
return selectCount(ErpProductDO::getCategoryId, categoryId);
}

@ -92,6 +92,13 @@ public interface ErpProductUnitService {
*/
List<ErpProductUnitDO> getProductUnitList();
/**
*
*
* @return
*/
List<ErpProductUnitDO> getProductUnitListByFlag();
/**
*
*

@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUn
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductUnitMapper;
import cn.iocoder.yudao.module.erp.framework.bean.ProductUnitEnum;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.annotations.VisibleForTesting;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -52,7 +53,7 @@ public class ErpProductUnitServiceImpl implements ErpProductUnitService {
updateReqVO.getId().equals(ProductUnitEnum.Kilogram.getUnitId())
|| updateReqVO.getId().equals(ProductUnitEnum.Gram.getUnitId())
|| updateReqVO.getId().equals(ProductUnitEnum.Each.getUnitId())){
throw exception(new ErrorCode(500,"内置单位不允许更改"));
throw exception(new ErrorCode(40001,"内置单位不允许更改"));
}
// 1.1 校验存在
validateProductUnitExists(updateReqVO.getId());
@ -129,4 +130,10 @@ public class ErpProductUnitServiceImpl implements ErpProductUnitService {
public ErpProductUnitDO getProductUnitByName(String name) {
return productUnitMapper.selectByName(name);
}
@Override
public List<ErpProductUnitDO> getProductUnitListByFlag() {
QueryWrapper<ErpProductUnitDO> wrapper = new QueryWrapper<>();
wrapper.eq("primary_flag", "Y");
return productUnitMapper.selectList(wrapper);
}
}

@ -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(

@ -42,9 +42,12 @@ public class DeviceModelAttributeRespVO {
private String address;
@Schema(description = "单位")
@ExcelProperty("单位")
private String dataUnit;
@Schema(description = "单位")
@ExcelProperty("单位")
private String dataUnitName;
@Schema(description = "倍率")
@ExcelProperty("倍率")
private Double ratio;

@ -56,4 +56,7 @@ public class RecipeRespVO {
// @ExcelProperty("关联设备ID")
private Long deviceId;
@Schema(description = "设备Id")
private Long machineId;
}

@ -37,4 +37,7 @@ public class RecipeSaveReqVO {
@Schema(description = "数据单位")
private String dataUnit;
@Schema(description = "设备Id")
private Long machineId;
}

@ -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<RecipeDeviceAttributeDO> attributeList = recipeDeviceAttributeService.getByRecipeId(recipePlanDetailDO.getRecipeId());
recipePlanDetailDO.setRecipeId(32L);
List<RecipeDeviceAttributeDO> attributeList = recipeDeviceAttributeService.getByRecipeId(recipePlanDetailDO.getRecipeId());
// if (CollectionUtils.isEmpty(attributeList)) {
// return success(false); // 无关联属性,直接返回
// }
Map<Long, Map<String, Object>> deviceDataMap = deviceService.createDeviceDataMap(103L);//recipeRespVO.getDeviceId()
//先删除在添加
List<RecipeDeviceRecordDO> 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<String, Object> data = deviceDataMap.get(attributeDO.getAttributeId());
if (data != null ) {
Map<Long, DeviceContactModelDO> deviceContactModelMap = new HashMap<>();
List<DeviceContactModelDO> 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);
}

@ -52,4 +52,5 @@ public class RecipeDeviceRecordPageReqVO extends PageParam {
@Schema(description = "配方id", example = "32535")
private Long recipeId;
}

@ -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 = "来源(新增/生产中)")

@ -60,4 +60,11 @@ public class RecipeDO extends BaseDO {
*/
private String dataUnit;
/**
*
*/
private Long machineId;
}

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

@ -53,4 +53,6 @@ public interface DeviceContactModelService {
PageResult<DeviceContactModelDO> getDeviceContactModelPage(DeviceContactModelPageReqVO pageReqVO);
List<DeviceContactModelDO> getDeviceContactModelList(Long id);
List<DeviceContactModelDO> selectListByDeviceId(Long id);
}

@ -99,4 +99,9 @@ public class DeviceContactModelServiceImpl implements DeviceContactModelService
.orderByDesc(DeviceContactModelDO::getCreateTime));
}
@Override
public List<DeviceContactModelDO> selectListByDeviceId(Long id) {
return deviceContactModelMapper.selectList(Wrappers.<DeviceContactModelDO>lambdaQuery().eq(DeviceContactModelDO::getDeviceId,id));
}
}

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.iot.service.devicemodelattribute;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.service.product.ErpProductUnitService;
import cn.iocoder.yudao.module.iot.dal.dataobject.deviceattributetype.DeviceAttributeTypeDO;
import cn.iocoder.yudao.module.iot.dal.mysql.deviceattributetype.DeviceAttributeTypeMapper;
import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants;
@ -32,6 +35,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.dal.mysql.devicemodelattribute.DeviceModelAttributeMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
/**
@ -53,6 +57,9 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
@Resource
private DeviceAttributeTypeMapper deviceAttributeTypeMapper;
@Resource
private ErpProductUnitService productUnitService;
@Override
public Long createDeviceModelAttribute(DeviceModelAttributeSaveReqVO createReqVO) {
@ -115,9 +122,33 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
// List<DeviceAttributeTypeDO> attributeTypes = deviceAttributeTypeMapper.selectList();
// Map<Long, String> typeNameMap = attributeTypes.stream()
// .collect(Collectors.toMap(DeviceAttributeTypeDO::getId, DeviceAttributeTypeDO::getName));
Map<Long, ErpProductUnitDO> unitMap = productUnitService.getProductUnitMap(
pageResult.getList().stream()
.map(DeviceModelAttributeDO::getDataUnit)
.filter(StringUtils::isNotEmpty)
.map(str -> {
try {
return Long.valueOf(str.trim());
} catch (NumberFormatException e) {
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet())
);
// 转换并设置类型名称
PageResult<DeviceModelAttributeRespVO> respPageResult = BeanUtils.toBean(pageResult, DeviceModelAttributeRespVO.class);
PageResult<DeviceModelAttributeRespVO> respPageResult = BeanUtils.toBean(pageResult, DeviceModelAttributeRespVO.class, deviceModelAttribute -> {
String dataUnitStr = deviceModelAttribute.getDataUnit();
if (StringUtils.isNotBlank(dataUnitStr)) {
try {
Long dataUnitId = Long.valueOf(dataUnitStr.trim());
MapUtils.findAndThen(unitMap, dataUnitId,
unit -> deviceModelAttribute.setDataUnitName(unit.getName()));
} catch (NumberFormatException e) {
}
}
});
// respPageResult.getList().forEach(item -> {
// String typeName = typeNameMap.get(item.getAttributeType());
@ -252,7 +283,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
importAttributes.forEach(importAttribute -> {
// 4.1 设置设备型号ID
importAttribute.setDeviceModelId(deviceModelId);
// 4.2 处理点位类型转换
String typeName = importAttribute.getTypeName();
if (StringUtils.isBlank(typeName)) {

@ -52,4 +52,7 @@ public interface RecipeDeviceRecordService {
*/
PageResult<RecipeDeviceRecordDO> getRecipeDeviceRecordPage(RecipeDeviceRecordPageReqVO pageReqVO);
List<RecipeDeviceRecordDO> getListByRecipeId(Long recipeId);
void deleteByIds(List<RecipeDeviceRecordDO> recipeDeviceRecordDOS);
}

@ -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<RecipeDeviceRecordDO> getListByRecipeId(Long recipeId) {
return recipeDeviceRecordMapper.selectList(Wrappers.<RecipeDeviceRecordDO>lambdaQuery().eq(RecipeDeviceRecordDO::getRecipeId, recipeId));
}
@Override
public void deleteByIds(List<RecipeDeviceRecordDO> recipeDeviceRecordDOS) {
recipeDeviceRecordMapper.deleteByIds(recipeDeviceRecordDOS);
}
}

@ -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<RecipePointRecordDO> recipePointRecordDOS = recipePointRecordMapper.selectList(Wrappers.<RecipePointRecordDO>lambdaQuery().eq(RecipePointRecordDO::getRecipeId, createReqVO.getRecipeId()));
if (!recipePointRecordDOS.isEmpty()){
recipePointRecordMapper.deleteByIds(recipePointRecordDOS);
}
// 插入
RecipePointRecordDO recipePointRecord = BeanUtils.toBean(createReqVO, RecipePointRecordDO.class);
recipePointRecordMapper.insert(recipePointRecord);

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