|
@@ -0,0 +1,445 @@
|
|
|
|
|
+package cn.sciento.farm.automationv2.app.service;
|
|
|
|
|
+
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.entity.IrrigationTask;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.entity.mongo.IrrigationTaskGroupNodeVO;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.entity.mongo.IrrigationTaskGroupVO;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.entity.mongo.IrrigationTaskLog;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.entity.mongo.IrrigationTaskMainVO;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.enums.NodeStatus;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.enums.NodeType;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.enums.TriggerType;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.enums.ZonePhase;
|
|
|
|
|
+import cn.sciento.farm.automationv2.domain.repository.IrrigationTaskLogRepository;
|
|
|
|
|
+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.domain.valueobject.ZoneConfigView;
|
|
|
|
|
+import cn.sciento.farm.automationv2.infra.constant.RedisConstant;
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 任务日志服务
|
|
|
|
|
+ * 负责灌溉任务日志的创建和更新
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Service
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class TaskLogService {
|
|
|
|
|
+
|
|
|
|
|
+ private final IrrigationTaskLogRepository taskLogRepository;
|
|
|
|
|
+
|
|
|
|
|
+ private final StringRedisTemplate stringRedisTemplate;
|
|
|
|
|
+
|
|
|
|
|
+ private static final long DEFAULT_TTL_HOUR = 3;
|
|
|
|
|
+
|
|
|
|
|
+ private void saveMain(Long taskId, IrrigationTaskMainVO mainVO) {
|
|
|
|
|
+ save(taskId, mainVO, DEFAULT_TTL_HOUR, TimeUnit.HOURS);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String buildKey(Long taskId) {
|
|
|
|
|
+ return RedisConstant.TASK_EXECUTION_LOG + taskId;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void save(Long taskId, IrrigationTaskMainVO mainVO, long ttl, TimeUnit unit) {
|
|
|
|
|
+ if (taskId == null || mainVO == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ String key = buildKey(taskId);
|
|
|
|
|
+ String json = JSON.toJSONString(mainVO);
|
|
|
|
|
+ stringRedisTemplate.opsForValue().set(key, json, ttl, unit);
|
|
|
|
|
+ log.debug("Saved ExecutionPlan to Redis, key={}, ttl={} {}", key, ttl, unit);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 初始化任务日志
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param task 任务信息
|
|
|
|
|
+ * @param executionId 执行实例ID
|
|
|
|
|
+ * @param triggerType 触发类型
|
|
|
|
|
+ * @param plan 执行计划
|
|
|
|
|
+ * @param zones 灌区配置列表
|
|
|
|
|
+ */
|
|
|
|
|
+ public void initializeLog(IrrigationTask task, Long executionId, TriggerType triggerType,
|
|
|
|
|
+ ExecutionPlan plan, List<ZoneConfigView> zones) {
|
|
|
|
|
+ log.info("初始化任务日志,executionId={}, taskId={}", executionId, task.getId());
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 构建主要数据
|
|
|
|
|
+ IrrigationTaskMainVO mainData = new IrrigationTaskMainVO();
|
|
|
|
|
+ mainData.setTriggerType(triggerType);
|
|
|
|
|
+ mainData.setExecutionId(executionId);
|
|
|
|
|
+ mainData.setTaskName(task.getTaskName());
|
|
|
|
|
+ mainData.setTotalPlanTime(calculateTotalPlanTime(plan));
|
|
|
|
|
+ mainData.setGroupName("预启动中");
|
|
|
|
|
+ mainData.setTotalGroup(zones.size());
|
|
|
|
|
+ mainData.setGroupNum(0);
|
|
|
|
|
+ // 存缓存
|
|
|
|
|
+ saveMain(task.getId(),mainData);
|
|
|
|
|
+
|
|
|
|
|
+ // 按 zoneIndex 分组节点
|
|
|
|
|
+ Map<Integer, List<ExecutionNode>> nodesByZone = plan.getNodes().stream()
|
|
|
|
|
+ .filter(node -> node.getZoneIndex() != null && node.getZoneIndex() >= 0)
|
|
|
|
|
+ .collect(Collectors.groupingBy(ExecutionNode::getZoneIndex));
|
|
|
|
|
+
|
|
|
|
|
+ // 构建灌区列表
|
|
|
|
|
+ List<IrrigationTaskGroupVO> groups = new ArrayList<>();
|
|
|
|
|
+ for (int i = 0; i < zones.size(); i++) {
|
|
|
|
|
+ ZoneConfigView zone = zones.get(i);
|
|
|
|
|
+ List<ExecutionNode> zoneNodes = nodesByZone.get(i);
|
|
|
|
|
+
|
|
|
|
|
+ IrrigationTaskGroupVO groupVO = new IrrigationTaskGroupVO();
|
|
|
|
|
+
|
|
|
|
|
+ // 直接从 ZoneConfigView 获取灌区名称
|
|
|
|
|
+ groupVO.setGroupName(zone.getGroupName());
|
|
|
|
|
+ groupVO.setPlanTime(zone.getIrrigationDuration());
|
|
|
|
|
+ groupVO.setStatus("PENDING");
|
|
|
|
|
+
|
|
|
|
|
+ // 构建节点列表
|
|
|
|
|
+ List<IrrigationTaskGroupNodeVO> nodeVOs = new ArrayList<>();
|
|
|
|
|
+ if (zoneNodes != null) {
|
|
|
|
|
+ for (ExecutionNode node : zoneNodes) {
|
|
|
|
|
+ // 只记录主要操作节点(开关阀、启停泵、设置压力)
|
|
|
|
|
+ if (shouldRecordNode(node)) {
|
|
|
|
|
+ List<IrrigationTaskGroupNodeVO> deviceNodes = convertNodeToDeviceNodes(node);
|
|
|
|
|
+ nodeVOs.addAll(deviceNodes);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ groupVO.setNodes(nodeVOs);
|
|
|
|
|
+
|
|
|
|
|
+ groups.add(groupVO);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建日志对象
|
|
|
|
|
+ IrrigationTaskLog log = new IrrigationTaskLog();
|
|
|
|
|
+ log.setMainData(mainData);
|
|
|
|
|
+ log.setGroups(groups);
|
|
|
|
|
+ log.setTaskId(task.getId());
|
|
|
|
|
+ log.setTenantId(task.getTenantId());
|
|
|
|
|
+ log.setOrganizationId(task.getOrganizationId());
|
|
|
|
|
+ log.setExecutionId(executionId);
|
|
|
|
|
+ log.setTaskBeginTime(LocalDateTime.now());
|
|
|
|
|
+
|
|
|
|
|
+ // 保存到MongoDB
|
|
|
|
|
+ taskLogRepository.save(log);
|
|
|
|
|
+
|
|
|
|
|
+ this.log.info("任务日志初始化成功,executionId={}, 灌区数量={}", executionId, groups.size());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ this.log.error("初始化任务日志失败,executionId={}", executionId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新节点开始执行
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param executionId 执行实例ID
|
|
|
|
|
+ * @param node 节点信息
|
|
|
|
|
+ */
|
|
|
|
|
+ public void updateNodeStart(Long executionId, ExecutionNode node) {
|
|
|
|
|
+ log.debug("更新节点开始执行,executionId={}, nodeIndex={}, nodeType={}",
|
|
|
|
|
+ executionId, node.getIndex(), node.getNodeType());
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Optional<IrrigationTaskLog> logOpt = taskLogRepository.findByExecutionId(executionId);
|
|
|
|
|
+ if (!logOpt.isPresent()) {
|
|
|
|
|
+ log.warn("任务日志不存在,executionId={}", executionId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ IrrigationTaskLog taskLog = logOpt.get();
|
|
|
|
|
+ Integer zoneIndex = node.getZoneIndex();
|
|
|
|
|
+ ZonePhase zonePhase = node.getZonePhase();
|
|
|
|
|
+
|
|
|
|
|
+ if (zoneIndex == null || zoneIndex < 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新灌区状态
|
|
|
|
|
+ if (taskLog.getGroups() != null && zoneIndex < taskLog.getGroups().size()) {
|
|
|
|
|
+ IrrigationTaskGroupVO group = taskLog.getGroups().get(zoneIndex);
|
|
|
|
|
+
|
|
|
|
|
+ // 根据 zonePhase 更新灌区状态
|
|
|
|
|
+ if (ZonePhase.STARTING == zonePhase) {
|
|
|
|
|
+ if (group.getGroupBeginTime() == null) {
|
|
|
|
|
+ group.setGroupBeginTime(LocalDateTime.now());
|
|
|
|
|
+ }
|
|
|
|
|
+ group.setStatus(ZonePhase.STARTING.name());
|
|
|
|
|
+ } else if (ZonePhase.IRRIGATING == zonePhase) {
|
|
|
|
|
+ group.setStatus(ZonePhase.IRRIGATING.name());
|
|
|
|
|
+ } else if (ZonePhase.SWITCHING == zonePhase) {
|
|
|
|
|
+ group.setStatus(ZonePhase.SWITCHING.name());
|
|
|
|
|
+ } else if (ZonePhase.STOPPING == zonePhase) {
|
|
|
|
|
+ group.setStatus(ZonePhase.STOPPING.name());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新节点状态
|
|
|
|
|
+ updateGroupNodeStatus(group, node, "RUNNING", LocalDateTime.now(), null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新主数据当前灌区
|
|
|
|
|
+ if (taskLog.getMainData() != null && taskLog.getGroups() != null && zoneIndex < taskLog.getGroups().size()) {
|
|
|
|
|
+ IrrigationTaskGroupVO currentGroup = taskLog.getGroups().get(zoneIndex);
|
|
|
|
|
+ taskLog.getMainData().setGroupName(currentGroup.getGroupName());
|
|
|
|
|
+ taskLog.getMainData().setGroupBeginTime(currentGroup.getGroupBeginTime());
|
|
|
|
|
+ taskLog.getMainData().setGroupNum(zoneIndex);
|
|
|
|
|
+ saveMain(taskLog.getTaskId(),taskLog.getMainData());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ taskLogRepository.save(taskLog);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("更新节点开始执行失败,executionId={}, nodeIndex={}",
|
|
|
|
|
+ executionId, node.getIndex(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新节点执行成功
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param executionId 执行实例ID
|
|
|
|
|
+ * @param node 节点信息
|
|
|
|
|
+ */
|
|
|
|
|
+ public void updateNodeSuccess(Long executionId, ExecutionNode node) {
|
|
|
|
|
+ log.debug("更新节点执行成功,executionId={}, nodeIndex={}, nodeType={}",
|
|
|
|
|
+ executionId, node.getIndex(), node.getNodeType());
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Optional<IrrigationTaskLog> logOpt = taskLogRepository.findByExecutionId(executionId);
|
|
|
|
|
+ if (!logOpt.isPresent()) {
|
|
|
|
|
+ log.warn("任务日志不存在,executionId={}", executionId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ IrrigationTaskLog taskLog = logOpt.get();
|
|
|
|
|
+ Integer zoneIndex = node.getZoneIndex();
|
|
|
|
|
+
|
|
|
|
|
+ if (zoneIndex == null || zoneIndex < 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新灌区状态
|
|
|
|
|
+ if (taskLog.getGroups() != null && zoneIndex < taskLog.getGroups().size()) {
|
|
|
|
|
+ IrrigationTaskGroupVO group = taskLog.getGroups().get(zoneIndex);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新节点状态
|
|
|
|
|
+ updateGroupNodeStatus(group, node, "SUCCESS", null, LocalDateTime.now());
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否是灌区的最后一个节点(CLOSE_GROUP 或最后的 STOPPING 阶段)
|
|
|
|
|
+ if (node.getNodeType() == NodeType.CLOSE_GROUP ||
|
|
|
|
|
+ (ZonePhase.STOPPING == node.getZonePhase() && NodeStatus.SUCCESS.name().equals(node.getStatus()))) {
|
|
|
|
|
+ group.setStatus("SUCCESS");
|
|
|
|
|
+ group.setGroupEndTime(LocalDateTime.now());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ taskLogRepository.save(taskLog);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("更新节点执行成功失败,executionId={}, nodeIndex={}",
|
|
|
|
|
+ executionId, node.getIndex(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 任务完成/失败
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param executionId 执行实例ID
|
|
|
|
|
+ */
|
|
|
|
|
+ public void updateTaskSuccess(Long executionId) {
|
|
|
|
|
+ Optional<IrrigationTaskLog> logOpt = taskLogRepository.findByExecutionId(executionId);
|
|
|
|
|
+ if (!logOpt.isPresent()) {
|
|
|
|
|
+ log.warn("任务日志不存在,executionId={}", executionId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ IrrigationTaskLog taskLog = logOpt.get();
|
|
|
|
|
+ taskLog.getMainData().setGroupNum(taskLog.getMainData().getTotalGroup());
|
|
|
|
|
+ saveMain(taskLog.getTaskId(),taskLog.getMainData());
|
|
|
|
|
+ taskLog.setTaskEndTime(LocalDateTime.now());
|
|
|
|
|
+ taskLogRepository.save(taskLog);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新节点执行失败
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param executionId 执行实例ID
|
|
|
|
|
+ * @param node 节点信息
|
|
|
|
|
+ * @param failReason 失败原因
|
|
|
|
|
+ */
|
|
|
|
|
+ public void updateNodeFailure(Long executionId, ExecutionNode node, String failReason) {
|
|
|
|
|
+ log.debug("更新节点执行失败,executionId={}, nodeIndex={}, nodeType={}, failReason={}",
|
|
|
|
|
+ executionId, node.getIndex(), node.getNodeType(), failReason);
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ Optional<IrrigationTaskLog> logOpt = taskLogRepository.findByExecutionId(executionId);
|
|
|
|
|
+ if (!logOpt.isPresent()) {
|
|
|
|
|
+ log.warn("任务日志不存在,executionId={}", executionId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ IrrigationTaskLog taskLog = logOpt.get();
|
|
|
|
|
+ Integer zoneIndex = node.getZoneIndex();
|
|
|
|
|
+
|
|
|
|
|
+ if (zoneIndex == null || zoneIndex < 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新灌区状态为失败
|
|
|
|
|
+ if (taskLog.getGroups() != null && zoneIndex < taskLog.getGroups().size()) {
|
|
|
|
|
+ IrrigationTaskGroupVO group = taskLog.getGroups().get(zoneIndex);
|
|
|
|
|
+ group.setStatus("FAILED");
|
|
|
|
|
+ group.setGroupEndTime(LocalDateTime.now());
|
|
|
|
|
+
|
|
|
|
|
+ // 更新节点状态
|
|
|
|
|
+ updateGroupNodeStatus(group, node, "FAILED", null, LocalDateTime.now());
|
|
|
|
|
+ }
|
|
|
|
|
+ // 失败视为 结束
|
|
|
|
|
+ taskLog.setTaskEndTime(LocalDateTime.now());
|
|
|
|
|
+ taskLogRepository.save(taskLog);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("更新节点执行失败失败,executionId={}, nodeIndex={}",
|
|
|
|
|
+ executionId, node.getIndex(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新灌区内节点的状态
|
|
|
|
|
+ */
|
|
|
|
|
+ private void updateGroupNodeStatus(IrrigationTaskGroupVO group, ExecutionNode node,
|
|
|
|
|
+ String status, LocalDateTime beginTime, LocalDateTime endTime) {
|
|
|
|
|
+ if (group.getNodes() == null || !shouldRecordNode(node)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查找匹配的节点并更新
|
|
|
|
|
+ for (IrrigationTaskGroupNodeVO nodeVO : group.getNodes()) {
|
|
|
|
|
+ // 根据设备ID和节点类型匹配
|
|
|
|
|
+ if (matchesNode(nodeVO, node)) {
|
|
|
|
|
+ nodeVO.setStatus(status);
|
|
|
|
|
+ if (beginTime != null) {
|
|
|
|
|
+ nodeVO.setDeviceBeginTime(beginTime);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (endTime != null) {
|
|
|
|
|
+ nodeVO.setDeviceEndTime(endTime);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 判断节点是否匹配
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean matchesNode(IrrigationTaskGroupNodeVO nodeVO, ExecutionNode node) {
|
|
|
|
|
+ if (node.getDevices() == null || node.getDevices().isEmpty()) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 通过节点类型和设备ID匹配
|
|
|
|
|
+ if (!node.getNodeType().equals(nodeVO.getAction())) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查设备ID是否匹配
|
|
|
|
|
+ for (DeviceInfo device : node.getDevices()) {
|
|
|
|
|
+ if (device.getDeviceId().toString().equals(nodeVO.getDeviceId())) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 判断是否需要记录该节点
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean shouldRecordNode(ExecutionNode node) {
|
|
|
|
|
+ NodeType type = node.getNodeType();
|
|
|
|
|
+ return type == NodeType.OPEN_GROUP ||
|
|
|
|
|
+ type == NodeType.CLOSE_GROUP ||
|
|
|
|
|
+ type == NodeType.START_PUMP ||
|
|
|
|
|
+ type == NodeType.STOP_PUMP ||
|
|
|
|
|
+ type == NodeType.SET_PUMP_PRESSURE;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将节点转换为设备节点VO列表
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<IrrigationTaskGroupNodeVO> convertNodeToDeviceNodes(ExecutionNode node) {
|
|
|
|
|
+ List<IrrigationTaskGroupNodeVO> result = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ if (node.getDevices() != null) {
|
|
|
|
|
+ for (DeviceInfo device : node.getDevices()) {
|
|
|
|
|
+ IrrigationTaskGroupNodeVO nodeVO = new IrrigationTaskGroupNodeVO();
|
|
|
|
|
+ nodeVO.setDeviceId(device.getDeviceId().toString());
|
|
|
|
|
+ nodeVO.setDeviceName(device.getDeviceName());
|
|
|
|
|
+ nodeVO.setDeviceType(device.getDeviceType());
|
|
|
|
|
+ nodeVO.setSw(device.getSw());
|
|
|
|
|
+ nodeVO.setAction(node.getNodeType());
|
|
|
|
|
+ nodeVO.setStatus("PENDING");
|
|
|
|
|
+ result.add(nodeVO);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据节点类型获取设备类型
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getDeviceType(NodeType nodeType) {
|
|
|
|
|
+ switch (nodeType) {
|
|
|
|
|
+ case OPEN_GROUP:
|
|
|
|
|
+ case CLOSE_GROUP:
|
|
|
|
|
+ return "BALL_VALVE";
|
|
|
|
|
+ case START_PUMP:
|
|
|
|
|
+ case STOP_PUMP:
|
|
|
|
|
+ case SET_PUMP_PRESSURE:
|
|
|
|
|
+ return "PUMP";
|
|
|
|
|
+ default:
|
|
|
|
|
+ return "UN_KNOW";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据节点类型获取操作动作
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getActionFromNodeType(NodeType nodeType) {
|
|
|
|
|
+ switch (nodeType) {
|
|
|
|
|
+ case OPEN_GROUP:
|
|
|
|
|
+ return "开启球阀";
|
|
|
|
|
+ case CLOSE_GROUP:
|
|
|
|
|
+ return "关闭球阀";
|
|
|
|
|
+ case START_PUMP:
|
|
|
|
|
+ return "启动水泵";
|
|
|
|
|
+ case STOP_PUMP:
|
|
|
|
|
+ return "停止水泵";
|
|
|
|
|
+ case SET_PUMP_PRESSURE:
|
|
|
|
|
+ return "设置水泵压力";
|
|
|
|
|
+ default:
|
|
|
|
|
+ return "未知操作";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算总计划灌溉时长(秒)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Long calculateTotalPlanTime(ExecutionPlan plan) {
|
|
|
|
|
+ if (plan.getNodes() == null) {
|
|
|
|
|
+ return 0L;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return plan.getNodes().stream()
|
|
|
|
|
+ .filter(node -> node.getNodeType() == NodeType.WAIT)
|
|
|
|
|
+ .mapToLong(node -> {
|
|
|
|
|
+ Integer waitSeconds = node.getWaitSeconds();
|
|
|
|
|
+ return waitSeconds != null ? waitSeconds : 0;
|
|
|
|
|
+ })
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|