暂时提交

master
lld 2026-03-06 22:12:54 +08:00
parent 88b2ba4619
commit 814dcc0f58
1 changed files with 151 additions and 100 deletions

View File

@ -1,13 +1,8 @@
package com.agri.quartz.task;
import com.agri.common.core.domain.entity.SysUser;
import com.agri.common.enums.TempCommandStatus;
import com.agri.common.utils.RollerTimeCalculator;
import com.agri.common.utils.TempJudgeUtil;
import com.agri.common.utils.TimeConvertUtil;
import com.agri.common.utils.TimeRangeUtil;
import com.agri.common.utils.*;
import com.agri.framework.config.MqttConfig;
import com.agri.framework.interceptor.FrontendControlHandler;
import com.agri.framework.manager.MqttAutoOffManager;
import com.agri.system.domain.SysAgriInfo;
import com.agri.system.domain.SysDevOperLog;
@ -16,9 +11,7 @@ import com.agri.system.service.ISysAgriInfoService;
import com.agri.system.service.ISysDevOperLogService;
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;
@ -33,7 +26,6 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
@ -207,122 +199,181 @@ public class RollerAutoTask {
}
/**
*
*/
private static final String LOCK_PREFIX = "lock:";
private static final String AUTO_MODE = "auto_mode";
private static final String CREATE_BY = "条件控制";
private static final int OP_SOURCE = 3; // 操作来源:条件控制
private static final int LOCK_ACQUIRED = 1; // 是否获取锁
/**
*
* @param imei
* @param agriName
* @param roller
* @param isFirstRun
* @param vent
* @param reservedLen
*/
private void sendOpenCommand(String imei, String agriName, String roller, boolean isFirstRun,
BigDecimal vent, BigDecimal reservedLen) {
try {
// 默认
BigDecimal openLen = vent;
// 是第一次需要vent+预留风口
if (isFirstRun) {
log.info("自动模式:设备【{}】开启自动模式。触发自动化条件,卷膜【{}】是今天第一次执行",imei,roller);
openLen = openLen.add(reservedLen);
// ========== 1. 前置参数校验(防御性判断) ==========
validateBaseParams(imei, agriName, roller);
if (vent == null) {
log.error("【开指令】设备{}卷膜{}风口大小为空,指令发送失败", imei, roller);
return;
}
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秒适配设备回执场景
);
if (lockSuccess == null || !lockSuccess) {
log.warn("【分布式锁】前端【自动模式】下触发【{}】操作设备【{}】的【{}】功能失败;可能其他用户正在操作此功能", roller, imei, funcType);
if (isFirstRun && reservedLen == null) {
log.error("【开指令】设备{}卷膜{}首次执行但预留风口为空,指令发送失败", imei, roller);
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", message);
// 获取运行时间
int runTime = RollerTimeCalculator.calculateRunTime(openLen);
if (runTime>0) {
autoOffManager.scheduleAutoOff(imei, roller+"k",runTime);
}
} catch (MqttException e) {
throw new RuntimeException(e);
// ========== 2. 计算开指令风口长度 ==========
BigDecimal openLen = vent;
if (isFirstRun) {
log.info("【自动模式】设备【{}】卷膜【{}】今日首次执行开指令,叠加预留风口{}", imei, roller, reservedLen);
openLen = openLen.add(reservedLen);
}
String funcType = roller + "k1";
String message = buildMqttMessage(funcType);
// ========== 3. 执行核心指令逻辑 ==========
executeCommand(imei, agriName, roller, funcType, message, openLen, true);
}
/**
*
* @param imei
* @param agriName
* @param roller
* @param isFirstRun
* @param 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);
// ========== 1. 前置参数校验 ==========
validateBaseParams(imei, agriName, roller);
if (isFirstRun) {
log.info("【关指令】设备{}卷膜{}今日首次执行,直接返回不发送关指令", imei, roller);
return;
}
if (vent == null) {
log.error("【关指令】设备{}卷膜{}风口大小为空,指令发送失败", imei, roller);
return;
}
// 3. 记录日志
log.info("【指令处理】前端【自动模式】下触发【{}】操作设备【{}】的【{}】功能, 指令:{}"
, roller, imei, funcType, message);
// ========== 2. 构建关指令参数 ==========
String funcType = roller + "g1";
String message = buildMqttMessage(funcType);
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);
if (runTime > 0) {
autoOffManager.scheduleAutoOff(imei, roller+"g",runTime);
// ========== 3. 执行核心指令逻辑 ==========
executeCommand(imei, agriName, roller, funcType, message, vent, false);
}
// ========== 抽取公共方法:减少代码冗余 ==========
/**
*
* @param imei ID
* @param agriName
* @param roller
*/
private void validateBaseParams(String imei, String agriName, String roller) {
if (StringUtils.isBlank(imei)) {
throw new IllegalArgumentException("设备IMEI不能为空");
}
if (StringUtils.isBlank(agriName)) {
log.warn("【指令】设备{}卷膜{}大棚名称为空,仍继续执行指令", imei, roller);
}
if (StringUtils.isBlank(roller)) {
throw new IllegalArgumentException("卷膜标识不能为空");
}
}
/**
* MQTT
* @param funcType roller1k1/roller1g1
* @return MQTT
*/
private String buildMqttMessage(String funcType) {
return String.format("{\"%s\":1}", funcType);
}
/**
* ++MQTT+
* @param imei ID
* @param agriName
* @param roller
* @param funcType
* @param message MQTT
* @param len
* @param isOpen
*/
private void executeCommand(String imei, String agriName, String roller, String funcType,
String message, BigDecimal len, boolean isOpen) {
String lockKey = LOCK_PREFIX + imei + ":" + funcType;
try {
// ========== 1. 获取分布式锁 ==========
Boolean lockSuccess = stringRedisTemplate.opsForValue().setIfAbsent(
lockKey, AUTO_MODE, dtuCtlLockTTL, TimeUnit.SECONDS
);
if (lockSuccess == null || !lockSuccess) {
log.warn("【分布式锁】自动模式下{}设备{}的{}功能失败,可能存在并发操作",
isOpen ? "开启" : "关闭", imei, funcType);
return;
}
// ========== 2. 记录操作日志 ==========
log.info("【指令处理】自动模式下触发{}设备{}的{}功能,指令:{}",
isOpen ? "开启" : "关闭", imei, funcType, message);
saveOperLog(imei, agriName, funcType, message, isOpen ? 1 : 0);
// ========== 3. 发布MQTT指令 ==========
mqttMessageSender.publish("dtu/" + imei + "/down", message);
// ========== 4. 计算运行时间并调度自动关 ==========
int runTime = RollerTimeCalculator.calculateRunTime(len);
if (runTime > 0) {
String autoOffKey = roller + (isOpen ? "k" : "g");
autoOffManager.scheduleAutoOff(imei, autoOffKey, runTime);
log.debug("【自动关调度】设备{}卷膜{}调度{}秒后自动关闭", imei, roller, runTime);
}
} catch (MqttException e) {
throw new RuntimeException(e);
// ========== 异常处理:记录详细日志,不抛运行时异常 ==========
log.error("【MQTT异常】{}设备{}的{}功能指令发布失败,指令:{}",
isOpen ? "开启" : "关闭", imei, funcType, message, e);
} catch (Exception e) {
// 兜底异常捕获避免未知异常导致锁无法释放依赖TTL兜底
log.error("【指令执行异常】{}设备{}的{}功能执行失败",
isOpen ? "开启" : "关闭", imei, funcType, e);
}
}
/**
*
* @param imei ID
* @param agriName
* @param funcCode
* @param payload
* @param opType 1-0-
*/
private void saveOperLog(String imei, String agriName, String funcCode, String payload, int opType) {
SysDevOperLog logDto = new SysDevOperLog();
logDto.setAgriName(agriName);
logDto.setImei(imei);
logDto.setFuncCode(funcCode);
logDto.setOpType(opType);
logDto.setOpSource(OP_SOURCE);
logDto.setPayload(payload);
logDto.setLockAcquired(LOCK_ACQUIRED); // 已获取锁
logDto.setLockHolder(AUTO_MODE);
logDto.setCreateBy(CREATE_BY);
// 可选:增加异常捕获,避免日志保存失败影响指令执行
try {
devOperLogService.save(logDto);
} catch (Exception e) {
log.error("【日志保存失败】设备{}功能{}日志保存失败", imei, funcCode, e);
}
}
}