diff --git a/agri-quartz/src/main/java/com/agri/quartz/task/RollerAutoTask.java b/agri-quartz/src/main/java/com/agri/quartz/task/RollerAutoTask.java index 99d4fb1..fe9e406 100644 --- a/agri-quartz/src/main/java/com/agri/quartz/task/RollerAutoTask.java +++ b/agri-quartz/src/main/java/com/agri/quartz/task/RollerAutoTask.java @@ -18,6 +18,7 @@ import com.agri.system.service.ISysDtuDataService; import com.agri.system.service.ISysRollerParamService; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import org.apache.commons.collections4.CollectionUtils; +import org.checkerframework.checker.units.qual.A; import org.eclipse.paho.client.mqttv3.MqttException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,13 +77,14 @@ public class RollerAutoTask { */ private static final Logger log = LoggerFactory.getLogger(RollerAutoTask.class); + @Value("${spring.mqtt.dtu-ctl-lock-ttl}") private int dtuCtlLockTTL; public void checkAutoTerm() { // 查询自动模式的大棚 List agriInfos = agriInfoService.lambdaQuery() - .select(SysAgriInfo::getImei) + .select(SysAgriInfo::getImei, SysAgriInfo::getAgriName) .eq(SysAgriInfo::getWorkMode, 1) .eq(SysAgriInfo::getIsDeleted, 0) .list(); @@ -114,68 +116,91 @@ public class RollerAutoTask { // 查询每个IMEI今天的第一条日志 Map> todayLogCountByImeiMap = devOperLogService.getTodayLogCountByImeiMap(imeiList); // 循环所有开启自动化的大棚 - for (String imei : imeiList) { + for (SysAgriInfo agriInfo : agriInfos) { + String imei = agriInfo.getImei(); + String agriName = agriInfo.getAgriName(); // 获取温湿度指定imei的数据 List> dtuDataInfo = dtuDataByImeiMap.get(imei); // 该大棚温湿度不存在 if (CollectionUtils.isEmpty(dtuDataInfo)) { // todo 该大棚下1分钟内无最新温湿度,怀疑离线 - break; + continue; } - // 获取今天对应imei的日志 - Map todayLogByRoller = todayLogCountByImeiMap.get(imei); - // 最后一条对应imei对应温度 + + // 最后一条对应imei对应温度 及时间 Map dtuData = dtuDataInfo.get(0); + // 求温度上报时间 + LocalDateTime dtuTime = TimeConvertUtil.strToLocalDateTimeSafe((String) dtuData.get("time")); + if (dtuTime == null) { + // todo 当前大棚温湿度时间为空 跳过 + continue; + } + // 获取当前imei下的所有参数设置以及卷膜自动化条件设置 Map> configTermByRollerMap = rollerTermMap.get(imei); if (configTermByRollerMap.isEmpty()) { // todo 当前大棚下没有设置条件或者参数 - break; + continue; } - configTermByRollerMap.forEach((config, terms) ->{ + + // 获取今天对应imei的日志 + Map todayLogByRoller = todayLogCountByImeiMap.get(imei); + + for (Map.Entry> configEntry : configTermByRollerMap.entrySet()) { + String roller = configEntry.getKey(); // 当前卷膜 + List terms = configEntry.getValue(); // 当前卷膜条件 // 每个卷膜分组只会有一个卷膜参数设置,所有取第一个即可 + if (terms == null || terms.isEmpty()) { + // todo 当前卷膜 无参数设置,跳过当前roller + continue; + } + // 获取卷膜参数 RollerTermVO rollerConfig = terms.get(0); - // 整体参数 - String refTempCode = rollerConfig.getRefTempCode(); - String roller = rollerConfig.getRoller(); // 卷膜 + String refTempCode = rollerConfig.getRefTempCode(); // 参考温度 BigDecimal ventTotalLen = rollerConfig.getVentTotalLen(); // 风口总长 BigDecimal reservedLen = rollerConfig.getReservedLen(); // 预留风口 - // 判断对应卷膜是否是今天第一次操作 + // 防御性判断:避免dtuData.get(refTempCode)为null + Object tempObj = dtuData.get(refTempCode); + if (tempObj == null) { + // todo 当前卷膜参考温度设置为空 + continue; + } + BigDecimal currentTemp = new BigDecimal(tempObj.toString()); + + // 每个roller单独定义isFirstRun(作用域:当前roller) boolean isFirstRun = true; + // 判断对应卷膜是否是今天第一次操作 // todayLogByRoller为空铁定第一次操作,否则,就是todayLogByRoller.getRoller为空是第一次操作 - if (!todayLogByRoller.isEmpty()) { + if (todayLogByRoller != null && !todayLogByRoller.isEmpty()) { Integer logOfRoller = todayLogByRoller.getOrDefault(roller, 0); - if (logOfRoller>0) { + // 只要当前Roller今日有操作记录,就不是首次 仅影响当前roller的isFirstRun + if (logOfRoller > 0) { isFirstRun = false; } } - terms.forEach(term -> { - // 求温度上报时间 - LocalDateTime dtuTime = TimeConvertUtil.strToLocalDateTimeSafe((String) dtuData.get("time")); - if (dtuTime == null) { - return; // 跳过该设备 - } + + // 遍历当前roller的所有term(改用普通for循环,可读性更高) + for (RollerTermVO term : terms) { // 判断该温度上报时间是否在该条件设置的时间范围内 boolean inRange = TimeRangeUtil.isTimeInRange(dtuTime, term.getStartTime(), term.getEndTime()); // 在范围内 if (inRange) { + //判断温度是否在适宜温度内 - String redTempCode = dtuData.get(refTempCode).toString(); - BigDecimal currentTemp = new BigDecimal(dtuData.get(redTempCode).toString()); TempCommandStatus tempCommandStatus = TempJudgeUtil.judgeTempCommand(currentTemp, term.getTemp()); - if (tempCommandStatus==TempCommandStatus.OPEN) { + if (tempCommandStatus == TempCommandStatus.OPEN) { // 开指令 - sendOpenCommand(imei, roller, isFirstRun, term.getVent(),reservedLen); + sendOpenCommand(imei,agriName, roller, isFirstRun, term.getVent(), reservedLen); isFirstRun = false; - } else if (tempCommandStatus==TempCommandStatus.CLOSE) { + } else if (tempCommandStatus == TempCommandStatus.CLOSE) { // 关指令 - sendCloseCommand(imei, roller, isFirstRun, term.getVent()); + sendCloseCommand(imei, agriName, roller, isFirstRun, term.getVent()); + isFirstRun = false; } } - // 不在掠过 - }); - }); + } + } } @@ -185,7 +210,7 @@ public class RollerAutoTask { * @param isFirstRun 是否第一次执行 * @param vent 每次执行的风口大小 */ - private void sendOpenCommand(String imei, String roller, boolean isFirstRun, + private void sendOpenCommand(String imei, String agriName, String roller, boolean isFirstRun, BigDecimal vent, BigDecimal reservedLen) { try { // 默认 @@ -197,7 +222,7 @@ public class RollerAutoTask { } String funcType = roller + "k1"; - + String message = "{\"" + funcType + "\":1}"; String lockKey = "lock:" + imei + ":" + funcType; Boolean lockSuccess = stringRedisTemplate.opsForValue().setIfAbsent( lockKey, "auto_mode", dtuCtlLockTTL, TimeUnit.SECONDS // 延长至15秒,适配设备回执场景 @@ -208,33 +233,33 @@ public class RollerAutoTask { } // 3. 记录日志 - log.info("【指令处理】前端{}于{}控制设备{}的{}功能,指令:{}", - clientId, LocalDateTime.now(), deviceId, funcType, payload); - SysUser sysUser = sysUserService.lambdaQuery() - .eq(SysUser::getClientId, clientId) - .one(); - String operator = "手动控制"; - if (sysUser!=null) { - operator = sysUser.getUserName(); - } - SysAgriInfo agriInfo = sysAgriInfoService.lambdaQuery() - .eq(SysAgriInfo::getImei, deviceId) - .one(); - String agriName = (agriInfo!=null && org.apache.commons.lang3.ObjectUtils.isNotEmpty(agriInfo.getAgriName()))?agriInfo.getAgriName():null; + log.info("【指令处理】前端【自动模式】下触发【{}】操作设备【{}】的【{}】功能, 指令:{}" + , roller, imei, funcType, message); + + SysDevOperLog logDto = new SysDevOperLog(); + // 大棚名称 logDto.setAgriName(agriName); - logDto.setImei(deviceId); + // imei + logDto.setImei(imei); + // 卷膜n logDto.setFuncCode(funcType); - logDto.setOpType(funcCodeMap.get(funcType)); - logDto.setOpSource(1); - logDto.setPayload(payload); + // 运行1 暂停0 + logDto.setOpType(1); + // 来源 条件控制 + logDto.setOpSource(3); + // 指令 + logDto.setPayload(message); + // 是否成功获取锁 logDto.setLockAcquired(1); - logDto.setLockHolder(clientId); - logDto.setCreateBy(operator); - sysDevOperLogService.save(logDto); + // 锁持有者 + logDto.setLockHolder("auto_mode"); + // 操作人 + logDto.setCreateBy("条件控制"); + devOperLogService.save(logDto); - mqttMessageSender.publish("dtu/"+imei+"/down", "{\""+funcType+"\":1}"); + mqttMessageSender.publish("dtu/"+imei+"/down", message); // 获取运行时间 int runTime = RollerTimeCalculator.calculateRunTime(openLen); if (runTime>0) { @@ -249,11 +274,47 @@ public class RollerAutoTask { * @param isFirstRun 是否第一次执行 * @param vent 每次执行的风口大小 */ - private void sendCloseCommand(String imei, String roller, boolean isFirstRun, BigDecimal vent) { + private void sendCloseCommand(String imei, String agriName, String roller, boolean isFirstRun, BigDecimal vent) { try { // 如果是第一次直接返回 if (isFirstRun) return; String funcType = roller + "g1"; + String message = "{\"" + funcType + "\":1}"; + String lockKey = "lock:" + imei + ":" + funcType; + Boolean lockSuccess = stringRedisTemplate.opsForValue().setIfAbsent( + lockKey, "auto_mode", dtuCtlLockTTL, TimeUnit.SECONDS // 延长至15秒,适配设备回执场景 + ); + if (lockSuccess == null || !lockSuccess) { + log.warn("【分布式锁】前端【自动模式】下触发【{}】操作设备【{}】的【{}】功能失败;可能其他用户正在操作此功能", roller, imei, funcType); + return; + } + + // 3. 记录日志 + log.info("【指令处理】前端【自动模式】下触发【{}】操作设备【{}】的【{}】功能, 指令:{}" + , roller, imei, funcType, message); + + + SysDevOperLog logDto = new SysDevOperLog(); + // 大棚名称 + logDto.setAgriName(agriName); + // imei + logDto.setImei(imei); + // 卷膜n + logDto.setFuncCode(funcType); + // 运行1 暂停0 + logDto.setOpType(1); + // 来源 条件控制 + logDto.setOpSource(3); + // 指令 + logDto.setPayload(message); + // 是否成功获取锁 + logDto.setLockAcquired(1); + // 锁持有者 + logDto.setLockHolder("auto_mode"); + // 操作人 + logDto.setCreateBy("条件控制"); + devOperLogService.save(logDto); + mqttMessageSender.publish("dtu/"+imei+"/down", "{\""+funcType+"\":1}"); // 获取运行时间 int runTime = RollerTimeCalculator.calculateRunTime(vent);