|
|
@@ -1,12 +1,16 @@
|
|
|
package cn.sciento.farm.automationv2.app.service;
|
|
|
|
|
|
+import cn.sciento.farm.automationv2.api.dto.GatewayControlDto;
|
|
|
+import cn.sciento.farm.automationv2.app.handler.DeviceControlHelper;
|
|
|
+import cn.sciento.farm.automationv2.domain.business.DeviceBusiness;
|
|
|
import cn.sciento.farm.automationv2.domain.entity.TaskExecution;
|
|
|
import cn.sciento.farm.automationv2.domain.enums.AckStatus;
|
|
|
+import cn.sciento.farm.automationv2.domain.repository.TaskExecutionRepository;
|
|
|
import cn.sciento.farm.automationv2.domain.valueobject.DeviceInfo;
|
|
|
import cn.sciento.farm.automationv2.domain.valueobject.ExecutionNode;
|
|
|
import cn.sciento.farm.automationv2.domain.valueobject.ExecutionPlan;
|
|
|
-import cn.sciento.farm.automationv2.infra.mq.producer.DeviceCommandProducer;
|
|
|
import cn.sciento.farm.automationv2.infra.redis.AckManager;
|
|
|
+import cn.sciento.farm.automationv2.infra.redis.ExecutionPlanStore;
|
|
|
import cn.sciento.farm.automationv2.infra.mapper.TaskExecutionMapper;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
@@ -23,29 +27,16 @@ import java.util.stream.Collectors;
|
|
|
* 1. 关闭施肥机
|
|
|
* 2. 关闭水泵
|
|
|
* 3. 关闭所有电磁阀和球阀
|
|
|
- *
|
|
|
- * 超时策略:
|
|
|
- * - 每个设备等待ACK最多30秒
|
|
|
- * - 设备关闭失败不中断流程,继续关闭其他设备
|
|
|
*/
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
|
public class SafeShutdownService {
|
|
|
|
|
|
- private final TaskExecutionMapper taskExecutionMapper;
|
|
|
- private final DeviceCommandProducer deviceCommandProducer;
|
|
|
+ private final TaskExecutionRepository taskExecutionRepository;
|
|
|
+ private final DeviceBusiness deviceBusiness;
|
|
|
private final AckManager ackManager;
|
|
|
-
|
|
|
- /**
|
|
|
- * ACK超时时间(毫秒)
|
|
|
- */
|
|
|
- private static final long ACK_TIMEOUT_MS = 30000; // 30秒
|
|
|
-
|
|
|
- /**
|
|
|
- * ACK检查间隔(毫秒)
|
|
|
- */
|
|
|
- private static final long ACK_CHECK_INTERVAL_MS = 1000; // 1秒
|
|
|
+ private final ExecutionPlanStore executionPlanStore;
|
|
|
|
|
|
/**
|
|
|
* 安全关闭所有已开启的设备
|
|
|
@@ -57,17 +48,21 @@ public class SafeShutdownService {
|
|
|
log.info("开始安全关闭设备,executionId={}", executionId);
|
|
|
|
|
|
// 加载执行实例
|
|
|
- TaskExecution execution = taskExecutionMapper.selectById(executionId);
|
|
|
+ TaskExecution execution = taskExecutionRepository.selectByPrimaryKey(executionId);
|
|
|
if (execution == null) {
|
|
|
log.error("执行实例不存在,executionId={}", executionId);
|
|
|
return ShutdownResult.error("执行实例不存在");
|
|
|
}
|
|
|
|
|
|
// 提取已开启的设备
|
|
|
- OpenedDevices openedDevices = extractOpenedDevices(execution.getExecutionPlan());
|
|
|
+ OpenedDevices openedDevices = extractOpenedDevices(executionId);
|
|
|
+ if (openedDevices == null) {
|
|
|
+ log.error("无法提取已开启设备,executionId={}", executionId);
|
|
|
+ return ShutdownResult.error("执行计划不存在");
|
|
|
+ }
|
|
|
|
|
|
- List<String> failedDevices = new ArrayList<>();
|
|
|
- List<String> successDevices = new ArrayList<>();
|
|
|
+ List<Long> failedDevices = new ArrayList<>();
|
|
|
+ List<Long> successDevices = new ArrayList<>();
|
|
|
|
|
|
// 步骤1: 关闭施肥机
|
|
|
if (openedDevices.fertilizer != null) {
|
|
|
@@ -127,52 +122,57 @@ public class SafeShutdownService {
|
|
|
* @return true-成功,false-失败
|
|
|
*/
|
|
|
private boolean closeDevice(Long executionId, DeviceInfo device, Long tenantId) {
|
|
|
- String deviceId = device.getDeviceId();
|
|
|
+ Long deviceId = device.getDeviceId();
|
|
|
String deviceType = device.getDeviceType();
|
|
|
|
|
|
try {
|
|
|
- // 下发关闭指令
|
|
|
+ // 注册ACK期望
|
|
|
+ ackManager.registerAck(executionId, -1, Collections.singletonList(device));
|
|
|
+
|
|
|
+ Integer result = null;
|
|
|
+ GatewayControlDto dto = null;
|
|
|
+
|
|
|
+ // 根据设备类型构造控制DTO并调用Feign
|
|
|
if ("PUMP".equals(deviceType)) {
|
|
|
- deviceCommandProducer.sendStopPumpCommand(executionId, -1, deviceId, tenantId);
|
|
|
+ dto = DeviceControlHelper.buildStopPumpDto(device, tenantId);
|
|
|
+ result = deviceBusiness.control(dto);
|
|
|
+ log.info("关闭水泵,executionId={}, deviceId={}, result={}", executionId, deviceId, result);
|
|
|
} else if ("FERTILIZER".equals(deviceType)) {
|
|
|
- deviceCommandProducer.sendStopFertilizerCommand(executionId, -1, deviceId, tenantId);
|
|
|
+ dto = DeviceControlHelper.buildStopFertilizerDto(device, tenantId);
|
|
|
+ result = deviceBusiness.control(dto);
|
|
|
+ log.info("关闭施肥机,executionId={}, deviceId={}, result={}", executionId, deviceId, result);
|
|
|
} else if ("SOLENOID_VALVE".equals(deviceType)) {
|
|
|
- deviceCommandProducer.sendCloseSolenoidValveCommand(executionId, -1, device, tenantId);
|
|
|
+ dto = DeviceControlHelper.buildCloseSolenoidValveDto(device, tenantId);
|
|
|
+ result = deviceBusiness.control(dto);
|
|
|
+ log.info("关闭电磁阀,executionId={}, deviceId={}, result={}", executionId, deviceId, result);
|
|
|
} else if ("BALL_VALVE".equals(deviceType)) {
|
|
|
// 球阀归零(角度=0)
|
|
|
- deviceCommandProducer.sendBallValveAngleCommand(executionId, -1, device, 0, tenantId);
|
|
|
+ dto = DeviceControlHelper.buildBallValveAngleDto(device, 0, tenantId);
|
|
|
+ result = deviceBusiness.control(dto);
|
|
|
+ log.info("球阀归零,executionId={}, deviceId={}, result={}", executionId, deviceId, result);
|
|
|
} else {
|
|
|
log.warn("未知设备类型,deviceId={}, deviceType={}", deviceId, deviceType);
|
|
|
+ ackManager.updateAckStatus(executionId, -1, deviceId,
|
|
|
+ AckStatus.FAIL, "未知设备类型: " + deviceType);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- // 注册ACK期望
|
|
|
- ackManager.registerAck(executionId, -1, Collections.singletonList(device));
|
|
|
-
|
|
|
- // 等待ACK响应(最多30秒)
|
|
|
- long startTime = System.currentTimeMillis();
|
|
|
- while (System.currentTimeMillis() - startTime < ACK_TIMEOUT_MS) {
|
|
|
- Map<String, AckStatus> ackMap = ackManager.checkAckStatus(executionId, -1,
|
|
|
- Collections.singletonList(device));
|
|
|
-
|
|
|
- AckStatus status = ackMap.get(deviceId);
|
|
|
- if (status == AckStatus.SUCCESS) {
|
|
|
- log.info("设备关闭成功,deviceId={}", deviceId);
|
|
|
- return true;
|
|
|
- } else if (status == AckStatus.FAIL || status == AckStatus.TIMEOUT) {
|
|
|
- log.error("设备关闭失败,deviceId={}, status={}", deviceId, status);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // 继续等待
|
|
|
- Thread.sleep(ACK_CHECK_INTERVAL_MS);
|
|
|
+ // 根据返回值更新ACK状态
|
|
|
+ if (result != null && result == 1) {
|
|
|
+ ackManager.updateAckStatus(executionId, -1, deviceId,
|
|
|
+ AckStatus.SUCCESS, null);
|
|
|
+ log.info("设备关闭成功,deviceId={}", deviceId);
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ ackManager.updateAckStatus(executionId, -1, deviceId,
|
|
|
+ AckStatus.FAIL, "设备控制返回失败,result=" + result);
|
|
|
+ log.error("设备关闭失败,deviceId={}, result={}", deviceId, result);
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
- // 超时
|
|
|
- log.error("设备关闭超时,deviceId={}", deviceId);
|
|
|
- return false;
|
|
|
-
|
|
|
} catch (Exception e) {
|
|
|
+ ackManager.updateAckStatus(executionId, -1, deviceId,
|
|
|
+ AckStatus.FAIL, "调用设备控制异常: " + e.getMessage());
|
|
|
log.error("关闭设备异常,deviceId={}", deviceId, e);
|
|
|
return false;
|
|
|
}
|
|
|
@@ -181,7 +181,14 @@ public class SafeShutdownService {
|
|
|
/**
|
|
|
* 从执行计划中提取已开启的设备
|
|
|
*/
|
|
|
- private OpenedDevices extractOpenedDevices(ExecutionPlan plan) {
|
|
|
+ private OpenedDevices extractOpenedDevices(Long executionId) {
|
|
|
+ // 从Redis获取执行计划
|
|
|
+ ExecutionPlan plan = executionPlanStore.get(executionId);
|
|
|
+ if (plan == null) {
|
|
|
+ log.error("执行计划不存在,executionId={}", executionId);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
OpenedDevices result = new OpenedDevices();
|
|
|
|
|
|
for (ExecutionNode node : plan.getSuccessNodes()) {
|
|
|
@@ -246,23 +253,23 @@ public class SafeShutdownService {
|
|
|
*/
|
|
|
public static class ShutdownResult {
|
|
|
private final boolean success;
|
|
|
- private final List<String> successDevices;
|
|
|
- private final List<String> failedDevices;
|
|
|
+ private final List<Long> successDevices;
|
|
|
+ private final List<Long> failedDevices;
|
|
|
private final String errorMessage;
|
|
|
|
|
|
- private ShutdownResult(boolean success, List<String> successDevices,
|
|
|
- List<String> failedDevices, String errorMessage) {
|
|
|
+ private ShutdownResult(boolean success, List<Long> successDevices,
|
|
|
+ List<Long> failedDevices, String errorMessage) {
|
|
|
this.success = success;
|
|
|
this.successDevices = successDevices;
|
|
|
this.failedDevices = failedDevices;
|
|
|
this.errorMessage = errorMessage;
|
|
|
}
|
|
|
|
|
|
- public static ShutdownResult success(List<String> successDevices) {
|
|
|
+ public static ShutdownResult success(List<Long> successDevices) {
|
|
|
return new ShutdownResult(true, successDevices, Collections.emptyList(), null);
|
|
|
}
|
|
|
|
|
|
- public static ShutdownResult partial(List<String> successDevices, List<String> failedDevices) {
|
|
|
+ public static ShutdownResult partial(List<Long> successDevices, List<Long> failedDevices) {
|
|
|
return new ShutdownResult(false, successDevices, failedDevices, "部分设备关闭失败");
|
|
|
}
|
|
|
|
|
|
@@ -275,11 +282,11 @@ public class SafeShutdownService {
|
|
|
return success;
|
|
|
}
|
|
|
|
|
|
- public List<String> getSuccessDevices() {
|
|
|
+ public List<Long> getSuccessDevices() {
|
|
|
return successDevices;
|
|
|
}
|
|
|
|
|
|
- public List<String> getFailedDevices() {
|
|
|
+ public List<Long> getFailedDevices() {
|
|
|
return failedDevices;
|
|
|
}
|
|
|
|