| | |
| | | package com.zy.asrs.task; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.baomidou.mybatisplus.mapper.Wrapper; |
| | | import com.core.common.Cools; |
| | |
| | | import com.zy.asrs.service.WrkMastLogService; |
| | | import com.zy.asrs.service.WrkMastService; |
| | | import com.zy.asrs.task.handler.AgvHandler; |
| | | import com.zy.common.constant.ApiInterfaceConstant; |
| | | import com.zy.common.properties.SchedulerProperties; |
| | | import com.zy.common.utils.HttpHandler; |
| | | import com.zy.system.entity.Config; |
| | | import com.zy.system.service.ConfigService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.IOException; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | | /** |
| | |
| | | * 每次只处理一个任务,避免高并发执行 |
| | | * 使用AtomicBoolean确保单线程执行循环 |
| | | */ |
| | | @Scheduled(cron = "0/5 * * * * ? ") |
| | | @Scheduled(cron = "0/15 * * * * ? ") |
| | | private void allocateSite() { |
| | | if (!schedulerProperties.isEnabled()) { |
| | | log.debug("定时任务allocateSite:调度器未启用,跳过执行"); |
| | |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | log.warn("定时任务allocateSite:延迟被中断", e); |
| | | isAllocateSite.set(false); |
| | | break; // 如果被中断,退出循环 |
| | | } |
| | | } |
| | |
| | | displayTaskId, task.getWrkNo(), task.getIoType(), task.getStaNo()); |
| | | boolean processed = agvHandler.callAgv(Collections.singletonList(task)); |
| | | |
| | | // 只有当任务成功处理(成功呼叫AGV,状态从7变为8)时,才更新lastProcessedTaskId |
| | | // 如果任务被跳过(站点被占用等),不更新lastProcessedTaskId,下次会重新尝试 |
| | | if (processed) { |
| | | lastProcessedTaskId = task.getId(); |
| | |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | log.warn("呼叫AGV定时任务:延迟被中断", e); |
| | | isAllocateSite.set(false); |
| | | break; // 如果被中断,退出循环 |
| | | } |
| | | } |
| | |
| | | if (!schedulerProperties.isEnabled()) { |
| | | return; |
| | | } |
| | | List<Task> taskList = taskService.selectList(new EntityWrapper<Task>().eq("wrk_sts", 9).eq("is_deleted", 0)); |
| | | List<Task> taskList = taskService.selectList(new EntityWrapper<Task>().in("wrk_sts", 9L,10L).eq("is_deleted", 0)); |
| | | if (taskList.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | if (agvTask.getIoType() != null && |
| | | (agvTask.getIoType() == 1 || agvTask.getIoType() == 10 || |
| | | agvTask.getIoType() == 53 || agvTask.getIoType() == 57)) { |
| | | |
| | | // taskId使用工作号(wrk_no),如果工作号为空则使用任务ID |
| | | String displayTaskId = (agvTask.getWrkNo() != null) ? String.valueOf(agvTask.getWrkNo()) : String.valueOf(agvTask.getId()); |
| | | |
| | | // 更新AGV任务状态为完成 |
| | | agvTask.setWrkSts(9L); |
| | | agvTask.setModiTime(now); |
| | | if (taskService.updateById(agvTask)) { |
| | | completedTasks.add(agvTask); |
| | | completedCount++; |
| | | // taskId使用工作号(wrk_no),如果工作号为空则使用任务ID |
| | | String displayTaskId = (agvTask.getWrkNo() != null) ? String.valueOf(agvTask.getWrkNo()) : String.valueOf(agvTask.getId()); |
| | | log.info("入库任务工作档已入库成功,完结AGV呼叫单,taskId:{},wrkNo:{},barcode:{}", |
| | | displayTaskId, wrkMast.getWrkNo(), wrkMast.getBarcode()); |
| | | } |
| | |
| | | ); |
| | | } |
| | | // 如果通过wrk_no没找到,且有条码,则通过条码查询 |
| | | // 注意:通过条码查询时,需要验证工作档是否真的已完成(通过wrk_no匹配),避免重复条码导致误判 |
| | | if (wrkMastLog == null && !Cools.isEmpty(agvTask.getBarcode())) { |
| | | List<WrkMastLog> logList = wrkMastLogService.selectList( |
| | | new EntityWrapper<WrkMastLog>().eq("barcode", agvTask.getBarcode()) |
| | | ); |
| | | if (!logList.isEmpty()) { |
| | | wrkMastLog = logList.get(0); // 取第一个 |
| | | // 构建查询条件:条码匹配,且历史档创建时间在AGV订单创建时间之后,且不超过AGV创建时间之后6小时 |
| | | Wrapper<WrkMastLog> logWrapper = new EntityWrapper<WrkMastLog>() |
| | | |
| | | .eq("barcode", agvTask.getBarcode()); |
| | | // 如果AGV订单有创建时间,添加时间过滤条件 |
| | | if (agvTask.getAppeTime() != null) { |
| | | // 计算AGV创建时间之后6小时的时间 |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.setTime(agvTask.getAppeTime()); |
| | | calendar.add(Calendar.HOUR_OF_DAY, 6); // 加6小时 |
| | | Date endTime = calendar.getTime(); |
| | | // 查询条件:历史档创建时间 >= AGV订单创建时间 且 <= AGV创建时间之后6小时 |
| | | logWrapper = logWrapper.ge("appe_time", agvTask.getAppeTime()) |
| | | .le("appe_time", endTime) |
| | | .eq("barcode", agvTask.getBarcode()) |
| | | ; |
| | | } |
| | | List<WrkMastLog> logList = wrkMastLogService.selectList(logWrapper); |
| | | // 过滤:只保留工作号匹配的历史档,避免重复条码导致误判 |
| | | for (WrkMastLog log : logList) { |
| | | if (agvTask.getWrkNo() != null && log.getWrkNo() != null && |
| | | log.getWrkNo().equals(agvTask.getWrkNo())) { |
| | | wrkMastLog = log; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | reason, displayTaskId, agvTask.getWrkNo(), agvTask.getBarcode(), agvTask.getStaNo()); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | // 检查订单创建时间,超过五分钟后才查询AGV订单状态 |
| | | Date appeTime = agvTask.getAppeTime(); |
| | | boolean shouldCheckAgvStatus = false; |
| | | if (appeTime != null) { |
| | | long timeDiff = now.getTime() - appeTime.getTime(); |
| | | long fiveMinutesInMillis = 5 * 60 * 1000; // 5分钟 |
| | | if (timeDiff >= fiveMinutesInMillis) { |
| | | shouldCheckAgvStatus = true; |
| | | } |
| | | } else { |
| | | // 如果没有创建时间,默认检查 |
| | | shouldCheckAgvStatus = true; |
| | | } |
| | | // taskId使用工作号(wrk_no),如果工作号为空则使用任务ID |
| | | String displayTaskId = (agvTask.getWrkNo() != null) ? String.valueOf(agvTask.getWrkNo()) : String.valueOf(agvTask.getId()); |
| | | // 如果订单创建超过五分钟,查询AGV订单状态 |
| | | if (shouldCheckAgvStatus) { |
| | | // queryAgvOrderStatus内部已经调用processAgvOrderStatus进行归类处理 |
| | | // 返回true表示应该完结订单,添加到完成列表 |
| | | boolean shouldComplete = queryAgvOrderStatus(agvTask, displayTaskId); |
| | | if (shouldComplete) { |
| | | completedTasks.add(agvTask); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | // 立即将完成的AGV任务转移到历史表,不保留在Task表中 |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 查询AGV订单状态并归类处理 |
| | | * @param agvTask AGV任务 |
| | | * @param displayTaskId 显示的任务ID |
| | | * @return true表示应该完结订单,false表示不应该完结(跳过),如果查询失败返回false |
| | | */ |
| | | private boolean queryAgvOrderStatus(Task agvTask, String displayTaskId) { |
| | | try { |
| | | // 构建订单ID,优先使用agvWrkNo,如果为空则使用T+wrkNo(向后兼容) |
| | | String orderId = agvTask.getAgvWrkNo(); |
| | | if (orderId == null || orderId.isEmpty()) { |
| | | if (agvTask.getWrkNo() != null) { |
| | | orderId = "T" + agvTask.getWrkNo(); |
| | | } else { |
| | | String errorMsg = String.format("查询AGV订单状态失败:任务ID:%s,agvWrkNo和wrkNo都为空", displayTaskId); |
| | | log.warn("查询AGV订单状态失败 - 任务ID:{},{}", displayTaskId, errorMsg); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 构建请求JSON |
| | | JSONObject requestJson = new JSONObject(); |
| | | requestJson.put("entityName", "ContainerTransportOrder"); |
| | | requestJson.put("id", orderId); |
| | | String requestBody = requestJson.toJSONString(); |
| | | |
| | | // 构建请求头 |
| | | Map<String, Object> headers = new HashMap<>(); |
| | | headers.put("xyy-app-id", "seer"); |
| | | headers.put("xyy-app-key", "123456"); |
| | | |
| | | // 发送请求 |
| | | String response = null; |
| | | try { |
| | | response = new HttpHandler.Builder() |
| | | .setUri(ApiInterfaceConstant.AGV_IP) |
| | | .setPath(ApiInterfaceConstant.AGV_FIND_ONE_PATH) |
| | | .setJson(requestBody) |
| | | .setHeaders(headers) |
| | | .build() |
| | | .doPost(); |
| | | } catch (IOException e) { |
| | | String errorMsg = String.format("查询AGV订单状态API调用失败:%s,请求:%s", e.getMessage(), requestBody); |
| | | log.error("查询AGV订单状态失败 - 任务ID:{},订单ID:{},{}", displayTaskId, orderId, errorMsg, e); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | |
| | | // 解析响应 |
| | | if (response == null || response.trim().isEmpty()) { |
| | | String errorMsg = String.format("查询AGV订单状态API返回为空,请求:%s", requestBody); |
| | | log.warn("查询AGV订单状态失败 - 任务ID:{},订单ID:{},{}", displayTaskId, orderId, errorMsg); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | JSONObject responseJson = JSON.parseObject(response); |
| | | |
| | | // 检查是否有错误码 |
| | | String code = responseJson.getString("code"); |
| | | if (code != null && "errNoSuchEntity".equals(code)) { |
| | | // 找不到业务对象,记录到errorMemo但忽略(不阻止后续处理) |
| | | String errorMsg = String.format("查询AGV订单状态:找不到订单(errNoSuchEntity),请求:%s,响应:%s", requestBody, response); |
| | | log.debug("查询AGV订单状态:找不到订单 - 任务ID:{},订单ID:{}", displayTaskId, orderId); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | |
| | | // 获取订单信息 |
| | | JSONObject entityValue = responseJson.getJSONObject("entityValue"); |
| | | if (entityValue != null) { |
| | | String status = entityValue.getString("status"); |
| | | if (status != null) { |
| | | log.info("查询AGV订单状态成功 - 任务ID:{},订单ID:{},状态:{}", displayTaskId, orderId, status); |
| | | // 查询后使用processAgvOrderStatus方法归类 |
| | | Date now = new Date(); |
| | | boolean shouldComplete = processAgvOrderStatus(agvTask, status, displayTaskId, now); |
| | | return shouldComplete; |
| | | } else { |
| | | String errorMsg = String.format("查询AGV订单状态响应中缺少status字段,请求:%s,响应:%s", requestBody, response); |
| | | log.warn("查询AGV订单状态失败 - 任务ID:{},订单ID:{},{}", displayTaskId, orderId, errorMsg); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | } else { |
| | | String errorMsg = String.format("查询AGV订单状态响应中缺少entityValue字段,请求:%s,响应:%s", requestBody, response); |
| | | log.warn("查询AGV订单状态失败 - 任务ID:{},订单ID:{},{}", displayTaskId, orderId, errorMsg); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | } catch (com.alibaba.fastjson.JSONException e) { |
| | | String errorMsg = String.format("解析AGV订单状态响应JSON失败:%s,请求:%s,响应:%s", e.getMessage(), requestBody, response); |
| | | log.error("查询AGV订单状态失败 - 任务ID:{},订单ID:{},{}", displayTaskId, orderId, errorMsg, e); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | } catch (Exception e) { |
| | | String errorMsg = String.format("查询AGV订单状态异常:%s", e.getMessage()); |
| | | log.error("查询AGV订单状态异常 - 任务ID:{},{}", displayTaskId, errorMsg, e); |
| | | agvTask.setErrorMemo2(errorMsg); |
| | | agvTask.setErrorTime2(new Date()); |
| | | taskService.updateById(agvTask); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理AGV订单状态 |
| | | * @param agvTask AGV任务 |
| | | * @param status 订单状态 |
| | | * @param displayTaskId 显示的任务ID |
| | | * @param now 当前时间 |
| | | * @return true表示应该完结订单,false表示不应该完结(跳过) |
| | | */ |
| | | private boolean processAgvOrderStatus(Task agvTask, String status, String displayTaskId, Date now) { |
| | | if (status == null) { |
| | | return false; // 状态为空,跳过 |
| | | } |
| | | |
| | | switch (status) { |
| | | case "Building": |
| | | // Building=未提交,正常没有此状态,忽略 |
| | | log.debug("AGV订单状态为Building(未提交),忽略 - 任务ID:{}", displayTaskId); |
| | | return true; // 继续处理 |
| | | case "Created": |
| | | // Created=已提交,等待期间,不处理 |
| | | log.debug("AGV订单状态为Created(已提交),等待期间,不处理 - 任务ID:{}", displayTaskId); |
| | | return false; // 不处理,跳过 |
| | | case "Assigned": |
| | | case "Assined": |
| | | // Assigned=已派车,正常状态,不处理 |
| | | log.debug("AGV订单状态为{}(已派车),正常状态,不处理 - 任务ID:{}", status, displayTaskId); |
| | | return false; // 不处理,跳过 |
| | | case "Failed": |
| | | case "Failde": |
| | | // Failed=失败,标记为失败订单 |
| | | log.warn("AGV订单状态为{}(失败),标记为失败订单 - 任务ID:{}", status, displayTaskId); |
| | | agvTask.setWrkSts(10L); |
| | | agvTask.setErrorTime(now); |
| | | agvTask.setErrorMemo(String.format("AGV订单状态为%s(失败)", status)); |
| | | taskService.updateById(agvTask); |
| | | return false; // 不处理,跳过 |
| | | case "Done": |
| | | // Done=已完成,完结订单 |
| | | log.info("AGV订单状态为Done(已完成),完结订单 - 任务ID:{}", displayTaskId); |
| | | return true; // 继续处理,完结订单 |
| | | case "Cancelled": |
| | | // Cancelled=取消,取消订单 |
| | | log.warn("AGV订单状态为Cancelled(取消),取消订单 - 任务ID:{}", displayTaskId); |
| | | agvTask.setWrkSts(10L); |
| | | agvTask.setErrorTime(now); |
| | | agvTask.setErrorMemo("AGV订单状态为Cancelled(取消)"); |
| | | taskService.updateById(agvTask); |
| | | return false; // 不处理,跳过 |
| | | default: |
| | | // 未知状态,记录日志但继续处理 |
| | | log.warn("AGV订单状态未知:{},继续处理 - 任务ID:{}", status, displayTaskId); |
| | | return true; // 继续处理 |
| | | } |
| | | } |
| | | |
| | | } |
| | | |