package com.zy.acs.manager.core; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.zy.acs.common.constant.RedisConstant; import com.zy.acs.common.utils.RedisSupport; import com.zy.acs.common.utils.Utils; import com.zy.acs.framework.common.Cools; import com.zy.acs.framework.common.R; import com.zy.acs.framework.common.SnowflakeIdWorker; import com.zy.acs.manager.common.annotation.OperationLog; import com.zy.acs.manager.common.domain.param.HandlerPublishParam; import com.zy.acs.manager.common.exception.BusinessException; import com.zy.acs.manager.core.service.*; import com.zy.acs.manager.core.service.astart.MapDataDispatcher; import com.zy.acs.manager.manager.entity.*; import com.zy.acs.manager.manager.enums.*; import com.zy.acs.manager.manager.service.*; import com.zy.acs.manager.system.controller.BaseController; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.Date; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; /** * Created by vincent on 8/1/2024 */ @Slf4j @RestController @RequestMapping("/api/handler") public class HandlerController extends BaseController { private final RedisSupport redis = RedisSupport.defaultRedisSupport; public static final String APP_KEY = "xltys1995"; @Autowired private AgvService agvService; @Autowired private AgvDetailService agvDetailService; @Autowired private TaskService taskService; @Autowired private MainService mainService; @Autowired private MainLockWrapService mainLockWrapService; @Autowired private SnowflakeIdWorker snowflakeIdWorker; @Autowired private CodeService codeService; @Autowired private JdbcTemplate jdbcTemplate; @Autowired private LocService locService; @Autowired private StaService staService; @Autowired private TrafficService trafficService; @Autowired private ThreadPoolRegulator threadPoolRegulator; @Autowired private MapDataDispatcher mapDataDispatcher; @Autowired private AvoidWaveCalculator avoidWaveCalculator; @Autowired private PatrolService patrolService; @PreAuthorize("hasAuthority('manager:agv:update')") @OperationLog("Locate All Agv") @PostMapping("/locateAllAgv") public synchronized R locateAllAgv() { final Integer MAP_DEFAULT_LEV = 1; redis.deleteValue(RedisConstant.AGV_MAP_ASTAR_DYNAMIC_FLAG, String.valueOf(MAP_DEFAULT_LEV)); avoidWaveCalculator.calcDynamicNodeWhenBoot(); return R.ok(); } @PreAuthorize("hasAuthority('manager:agv:update')") @PostMapping("/patrol/batch/startup") public synchronized R patrolBatchStartup() { List list = agvService.list(new LambdaQueryWrapper().eq(Agv::getStatus, StatusType.ENABLE.val)); int result = 0; for (Agv agv : list) { patrolService.startupPatrol(agv.getUuid()); result++; } return R.ok().add(result); } @PreAuthorize("hasAuthority('manager:agv:update')") @PostMapping("/patrol/batch/shutdown") public synchronized R patrolBatchShutdown() { List list = agvService.list(new LambdaQueryWrapper()); for (String agvNo : list.stream().map(Agv::getUuid).collect(Collectors.toList())) { if (patrolService.isPatrolling(agvNo)) { patrolService.shutdownPatrol(agvNo); } } return R.ok(); } @RequestMapping(value = "/control/agv", method = {RequestMethod.GET, RequestMethod.POST}) @Transactional public R controlAgv(@RequestHeader String appKey, @RequestBody HandlerPublishParam param) { if (Cools.isEmpty(param.getAgvNo(), param.getTaskMode(), appKey)) { return R.error(); } if (!APP_KEY.equals(appKey)) { return R.error(); } Date now = new Date(); Agv agv = agvService.selectByUuid(param.getAgvNo()); AgvDetail agvDetail = agvDetailService.selectByAgvId(agv.getId()); // check if (taskService.count(new LambdaQueryWrapper() .eq(Task::getAgvId, agv.getId()) .and(i -> { i.eq(Task::getTaskSts, TaskStsType.WAITING.val()) .or() .eq(Task::getTaskSts, TaskStsType.ASSIGN.val()) .or().eq(Task::getTaskSts, TaskStsType.PROGRESS.val()); })) > 0) { log.info(agv.getUuid() + "号AGV不可用,已经存在进行中的任务..."); return R.error(); } if (!agvService.judgeEnable(agv.getId())) { log.info(agv.getUuid() + "号AGV不可用,任务无法计算..."); return R.error(); } // generate Loc oriLoc = null; Loc destLoc = null; Sta oriSta = null; Sta destSta = null; Task task = null; switch (param.getTaskMode()) { case MOVE: Code endCode = null; if (!Cools.isEmpty(param.getEndCode())) { endCode = codeService.getCacheById(param.getEndCode()); } if (!Cools.isEmpty(param.getEndCodeStr())) { endCode = codeService.getCacheByData(param.getEndCodeStr()); } if (null == endCode) { return R.error(); } if (!mainLockWrapService.buildMinorTask(agv.getId(), param.getTaskMode(), endCode.getData(), null)) { return R.error(); } break; case TO_CHARGE: case TO_STANDBY: if (!mainLockWrapService.buildMinorTask(agv.getId(), param.getTaskMode(), null, null)) { return R.error(); } break; case LOC_TO_LOC: // oriLoc if (!Cools.isEmpty(param.getStartLocNo())) { oriLoc = locService.getById(param.getStartLocNo()); } if (!Cools.isEmpty(param.getStartLocNoStr())) { oriLoc = locService.selecatByLocNo(param.getStartLocNoStr()); } if (null == oriLoc) { return R.error(); } if (!oriLoc.getLocSts().equals(LocStsType.STOCK.val())) { throw new BusinessException("oriLoc:" + oriLoc.getLocNo() + " 不是在库状态"); } oriLoc.setLocSts(LocStsType.PAKOUT.val()); oriLoc.setUpdateTime(now); if (!locService.updateById(oriLoc)) { throw new BusinessException("oriLoc:" + oriLoc.getLocNo() + " 修改库位状态失败"); } // destLoc if (!Cools.isEmpty(param.getEndLocNo())) { destLoc = locService.getById(param.getEndLocNo()); } if (!Cools.isEmpty(param.getEndLocNoStr())) { destLoc = locService.selecatByLocNo(param.getEndLocNoStr()); } if (null == destLoc) { return R.error(); } if (!destLoc.getLocSts().equals(LocStsType.IDLE.val())) { throw new BusinessException("destLoc:" + destLoc.getLocNo() + " 不是空闲状态"); } destLoc.setLocSts(LocStsType.PAKIN.val()); destLoc.setUpdateTime(now); if (!locService.updateById(destLoc)) { throw new BusinessException("destLoc:" + destLoc.getLocNo() + " 修改库位状态失败"); } // task task = new Task(); task.setOriLoc(oriLoc.getId()); task.setOriCode(oriLoc.getCode()); task.setDestLoc(destLoc.getId()); task.setDestCode(destLoc.getCode()); break; case LOC_TO_STA: // oriLoc if (!Cools.isEmpty(param.getStartLocNo())) { oriLoc = locService.getById(param.getStartLocNo()); } if (!Cools.isEmpty(param.getStartLocNoStr())) { oriLoc = locService.selecatByLocNo(param.getStartLocNoStr()); } if (null == oriLoc) { return R.error(); } if (!oriLoc.getLocSts().equals(LocStsType.STOCK.val())) { throw new BusinessException("oriLoc:" + oriLoc.getLocNo() + " 不是在库状态"); } oriLoc.setLocSts(LocStsType.PAKOUT.val()); oriLoc.setUpdateTime(now); if (!locService.updateById(oriLoc)) { throw new BusinessException("oriLoc:" + oriLoc.getLocNo() + " 修改库位状态失败"); } // destSta if (!Cools.isEmpty(param.getEndStaNo())) { destSta = staService.getById(param.getEndStaNo()); } if (!Cools.isEmpty(param.getEndStaNoStr())) { destSta = staService.selectByStaNo(param.getEndStaNoStr()); } if (null == destSta) { return R.error(); } if (!destSta.getStaSts().equals(StaStsType.IDLE.val())) { throw new BusinessException("destSta:" + destSta.getStaNo() + " 不是无货状态"); } destSta.setStaSts(StaStsType.READY_RELEASE.val()); destSta.setUpdateTime(now); if (!staService.updateById(destSta)) { throw new BusinessException("destSta:" + destSta.getStaNo() + " 修改站点状态失败"); } // task task = new Task(); task.setOriLoc(oriLoc.getId()); task.setOriCode(oriLoc.getCode()); task.setDestSta(destSta.getId()); task.setDestCode(destSta.getCode()); break; case STA_TO_LOC: // oriSta if (!Cools.isEmpty(param.getStartStaNo())) { oriSta = staService.getById(param.getStartStaNo()); } if (!Cools.isEmpty(param.getStartStaNoStr())) { oriSta = staService.selectByStaNo(param.getStartStaNoStr()); } if (null == oriSta) { return R.error(); } if (!oriSta.getStaSts().equals(StaStsType.STOCK.val())) { throw new BusinessException("oriSta:" + oriSta.getStaNo() + " 不是有货状态"); } oriSta.setStaSts(StaStsType.READY_TAKE.val()); oriSta.setUpdateTime(now); if (!staService.updateById(oriSta)) { throw new BusinessException("oriSta:" + oriSta.getStaNo() + " 修改站点状态失败"); } // destLoc if (!Cools.isEmpty(param.getEndLocNo())) { destLoc = locService.getById(param.getEndLocNo()); } if (!Cools.isEmpty(param.getEndLocNoStr())) { destLoc = locService.selecatByLocNo(param.getEndLocNoStr()); } if (null == destLoc) { return R.error(); } if (!destLoc.getLocSts().equals(LocStsType.IDLE.val())) { throw new BusinessException("destLoc:" + destLoc.getLocNo() + " 不是空闲状态"); } destLoc.setLocSts(LocStsType.PAKIN.val()); destLoc.setUpdateTime(now); if (!locService.updateById(destLoc)) { throw new BusinessException("destLoc:" + destLoc.getLocNo() + " 修改库位状态失败"); } // task task = new Task(); task.setOriSta(oriSta.getId()); task.setOriCode(oriSta.getCode()); task.setDestLoc(destLoc.getId()); task.setDestCode(destLoc.getCode()); break; case STA_TO_STA: // oriSta if (!Cools.isEmpty(param.getStartStaNo())) { oriSta = staService.getById(param.getStartStaNo()); } if (!Cools.isEmpty(param.getStartStaNoStr())) { oriSta = staService.selectByStaNo(param.getStartStaNoStr()); } if (null == oriSta) { return R.error(); } if (!oriSta.getStaSts().equals(StaStsType.STOCK.val())) { throw new BusinessException("oriSta:" + oriSta.getStaNo() + " 不是有货状态"); } oriSta.setStaSts(StaStsType.READY_TAKE.val()); oriSta.setUpdateTime(now); if (!staService.updateById(oriSta)) { throw new BusinessException("oriSta:" + oriSta.getStaNo() + " 修改站点状态失败"); } // destSta if (!Cools.isEmpty(param.getEndStaNo())) { destSta = staService.getById(param.getEndStaNo()); } if (!Cools.isEmpty(param.getEndStaNoStr())) { destSta = staService.selectByStaNo(param.getEndStaNoStr()); } if (null == destSta) { return R.error(); } if (!destSta.getStaSts().equals(StaStsType.IDLE.val())) { throw new BusinessException("destSta:" + destSta.getStaNo() + " 不是无货状态"); } destSta.setStaSts(StaStsType.READY_RELEASE.val()); destSta.setUpdateTime(now); if (!staService.updateById(destSta)) { throw new BusinessException("destSta:" + destSta.getStaNo() + " 修改站点状态失败"); } // task task = new Task(); task.setOriSta(oriSta.getId()); task.setOriCode(oriSta.getCode()); task.setDestSta(destSta.getId()); task.setDestCode(destSta.getCode()); break; default: break; } if (null != task) { task.setAgvId(agv.getId()); task.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); List lastTasks = taskService.list(new LambdaQueryWrapper().orderByDesc(Task::getId)); task.setSeqNum(Utils.generateSeqNum(Cools.isEmpty(lastTasks)?null:lastTasks.get(0).getSeqNum())); task.setTaskType(param.getTaskMode().val()); task.setTaskSts(TaskStsType.WAITING.val()); task.setPriority(999); task.setIoTime(now); task.setStartTime(now); if (!taskService.save(task)) { throw new BusinessException(task.getSeqNum() + "任务保存失败"); } } return R.ok(); } @RequestMapping(value = "/restore/agv", method = {RequestMethod.GET, RequestMethod.POST}) public R restoreAgv(@RequestHeader String appKey, @RequestBody HandlerPublishParam param) { if (Cools.isEmpty(appKey)) { return R.error(); } if (!APP_KEY.equals(appKey)) { return R.error(); } Agv agv = null; if (!Cools.isEmpty(param.getAgvNo())) { agv = agvService.selectByUuid(param.getAgvNo()); List tasks = taskService.selectInSts(agv.getId(), TaskStsType.WAITING, TaskStsType.ASSIGN, TaskStsType.PROGRESS); if (!Cools.isEmpty(tasks)) { for (Task task : tasks) { if (TaskTypeType.LOC_TO_LOC.toString().equals(task.getTaskTypeEl()) || TaskTypeType.LOC_TO_STA.toString().equals(task.getTaskTypeEl()) || TaskTypeType.STA_TO_LOC.toString().equals(task.getTaskTypeEl()) || TaskTypeType.STA_TO_STA.toString().equals(task.getTaskTypeEl()) ) { return R.error("Restore Failed, because the agv has Tasks that are associated with Loc!"); } } } } if (null != agv) { mapDataDispatcher.modifyDynamicMatrix(null, null, agv.getUuid(), true); avoidWaveCalculator.calcDynamicNodeByVehicle(agv, null); jdbcTemplate.update("UPDATE man_task SET task_sts = " + TaskStsType.COMPLETE.val() + " where agv_id = " + agv.getId()); jdbcTemplate.update("UPDATE man_travel SET state = 'FINISH'" + " where agv_id = " + agv.getId()); jdbcTemplate.update("UPDATE man_segment SET state = 'FINISH'" + " where agv_id = " + agv.getId()); jdbcTemplate.update("UPDATE man_action SET action_sts = " + ActionStsType.FINISH.val() + " where agv_id = " + agv.getId()); } else { List agvList = agvService.list(new LambdaQueryWrapper()); for (Agv one : agvList) { mapDataDispatcher.modifyDynamicMatrix(null, null, one.getUuid(), true); avoidWaveCalculator.calcDynamicNodeByVehicle(one, null); } jdbcTemplate.update("UPDATE man_task SET task_sts = " + TaskStsType.COMPLETE.val()); jdbcTemplate.update("UPDATE man_travel SET state = 'FINISH'"); jdbcTemplate.update("UPDATE man_segment SET state = 'FINISH'"); jdbcTemplate.update("UPDATE man_action SET action_sts = " + ActionStsType.FINISH.val()); } return R.ok(); } @RequestMapping(value = "/unlock", method = {RequestMethod.GET, RequestMethod.POST}) public R unlockPath(@RequestHeader String appKey, @RequestBody HandlerPublishParam param) throws ExecutionException, InterruptedException { if (Cools.isEmpty(param.getAgvNo(), appKey)) { return R.error(); } if (!APP_KEY.equals(appKey)) { return R.error(); } Agv agv = agvService.selectByUuid(param.getAgvNo()); // block // Future future = threadPoolRegulator.getInstance().submit(() -> { // mapDataDispatcher.modifyDynamicMatrix(null, null, param.getAgvNo(), true); // return success(); // }); // System.out.println(JSON.toJSONString(future.get())); // non-block CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> { mapDataDispatcher.modifyDynamicMatrix(null, null, param.getAgvNo(), true); avoidWaveCalculator.calcDynamicNodeByVehicle(agv, null); return R.ok(); }, threadPoolRegulator.getInstance()); completableFuture.thenAccept(result -> { System.out.println(JSON.toJSONString(result)); }).exceptionally(e -> { log.error("unlockPath", e); return null; }); return R.ok(); } @RequestMapping(value = "/agv/patrol", method = {RequestMethod.GET, RequestMethod.POST}) public R agvPatrol(@RequestHeader String appKey, @RequestBody HandlerPublishParam param) throws ExecutionException, InterruptedException { if (Cools.isEmpty(param.getAgvNo(), appKey)) { return R.error(); } if (!APP_KEY.equals(appKey)) { return R.error(); } String agvNo = param.getAgvNo(); if (patrolService.isPatrolling(agvNo)) { return patrolService.shutdownPatrol(agvNo); } else { return patrolService.startupPatrol(agvNo); } } }