开发移库任务

This commit is contained in:
cbs 2025-01-23 11:55:40 +08:00
parent e2f46b2978
commit 7ed63a7388
39 changed files with 1327 additions and 41 deletions

View File

@ -195,6 +195,7 @@ public interface ErrorCodeConstants {
ErrorCode TASK_CHECK_TASK_PRIORITY = new ErrorCode(1-002-035-007, "非新单据不能修改优先级");
ErrorCode TASK_CHECK_TASK_STATUS = new ErrorCode(1-002-035-100, "订单已完成");
ErrorCode TASK_CHECK_UPDATE_STATUS = new ErrorCode(1-002-035-101, "订单更新失败");
ErrorCode TASK_CHECK_EXIST_NO = new ErrorCode(1-002-035-102, "订单号已存在");
// ========== 机器人任务明细 1-002-036-000 ==========
ErrorCode TASK_DETAIL_NOT_EXISTS = new ErrorCode(1-002-036-001, "机器人任务明细不存在");
@ -213,4 +214,7 @@ public interface ErrorCodeConstants {
ErrorCode THERE_ARE_ALREADY_STORAGE_LOCATIONS_IN_OTHER_LINE_WAREHOUSES = new ErrorCode(1_002_038_007, "已有库位在其他线库内");
ErrorCode THERE_ARE_ALREADY_STORAGE_LOCATIONS_IN_OTHER_STORAGE_AREAS = new ErrorCode(1_002_038_008, "已有库位在其他库区内");
// ========== 机器人自动移库 1-002-039-000 ==========
ErrorCode TASK_AUTO_MOVE_NOT_EXISTS = new ErrorCode(1_002_039_001, "机器人自动移库不存在");
}

View File

@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.system.controller.admin.robot;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMovePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMoveRespVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMoveSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskAutoMoveDO;
import cn.iocoder.yudao.module.system.service.robot.job.RobotTaskAutoMoveService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
@Tag(name = "管理后台 - 机器人自动移库")
@RestController
@RequestMapping("/robot/task-auto-move")
@Validated
public class RobotTaskAutoMoveController {
@Resource
private RobotTaskAutoMoveService taskAutoMoveService;
@PostMapping("/create")
@Operation(summary = "创建机器人自动移库")
@PreAuthorize("@ss.hasPermission('robot:task-auto-move:create')")
public CommonResult<Long> createTaskAutoMove(@Valid @RequestBody RobotTaskAutoMoveSaveReqVO createReqVO) {
return success(taskAutoMoveService.createTaskAutoMove(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新机器人自动移库")
@PreAuthorize("@ss.hasPermission('robot:task-auto-move:update')")
public CommonResult<Boolean> updateTaskAutoMove(@Valid @RequestBody RobotTaskAutoMoveSaveReqVO updateReqVO) {
taskAutoMoveService.updateTaskAutoMove(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除机器人自动移库")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('robot:task-auto-move:delete')")
public CommonResult<Boolean> deleteTaskAutoMove(@RequestParam("id") Long id) {
taskAutoMoveService.deleteTaskAutoMove(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得机器人自动移库")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('robot:task-auto-move:query')")
public CommonResult<RobotTaskAutoMoveRespVO> getTaskAutoMove(@RequestParam("id") Long id) {
RobotTaskAutoMoveDO taskAutoMove = taskAutoMoveService.getTaskAutoMove(id);
return success(BeanUtils.toBean(taskAutoMove, RobotTaskAutoMoveRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得机器人自动移库分页")
@PreAuthorize("@ss.hasPermission('robot:task-auto-move:query')")
public CommonResult<PageResult<RobotTaskAutoMoveRespVO>> getTaskAutoMovePage(@Valid RobotTaskAutoMovePageReqVO pageReqVO) {
PageResult<RobotTaskAutoMoveDO> pageResult = taskAutoMoveService.getTaskAutoMovePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, RobotTaskAutoMoveRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出机器人自动移库 Excel")
@PreAuthorize("@ss.hasPermission('robot:task-auto-move:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportTaskAutoMoveExcel(@Valid RobotTaskAutoMovePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<RobotTaskAutoMoveDO> list = taskAutoMoveService.getTaskAutoMovePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "机器人自动移库.xls", "数据", RobotTaskAutoMoveRespVO.class,
BeanUtils.toBean(list, RobotTaskAutoMoveRespVO.class));
}
}

View File

@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.system.controller.admin.robot.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 机器人自动移库分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RobotTaskAutoMovePageReqVO extends PageParam {
@Schema(description = "机器人任务主表id", example = "24553")
private Long robotTaskId;
@Schema(description = "机器人任务明细表id", example = "11392")
private Long robotTaskDetailId;
@Schema(description = "原库位编号")
private String fromLocationNo;
@Schema(description = "原库位id", example = "8838")
private Long fromLocationId;
@Schema(description = "搬去的目标库位编号")
private String toLocationNo;
@Schema(description = "搬去的目标库位id/停车点/充电点/移动终点", example = "19168")
private Long toLocationId;
@Schema(description = "任务状态(0:未开始、1执行中、2已完成、3已取消、4异常)", example = "1")
private Integer taskStatus;
@Schema(description = "取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id(目前设计不会用到此字段)")
private Long toLaneId;
@Schema(description = "优先级")
private Long priority;
@Schema(description = "AGV编号")
private String robotNo;
@Schema(description = "任务类型1自动移库、 ", example = "2")
private Integer taskType;
@Schema(description = "移动类型1去、 2", example = "2")
private Integer moveType;
@Schema(description = "开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] startTime;
@Schema(description = "结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] endTime;
@Schema(description = "robot_task_auto_move表id来回搬运需要记录上一次搬运的源库位", example = "32231")
private Long taskAutoMoveId;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,87 @@
package cn.iocoder.yudao.module.system.controller.admin.robot.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 机器人自动移库 Response VO")
@Data
@ExcelIgnoreUnannotated
public class RobotTaskAutoMoveRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30110")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "机器人任务主表id", example = "24553")
@ExcelProperty("机器人任务主表id")
private Long robotTaskId;
@Schema(description = "机器人任务明细表id", example = "11392")
@ExcelProperty("机器人任务明细表id")
private Long robotTaskDetailId;
@Schema(description = "原库位编号")
@ExcelProperty("原库位编号")
private String fromLocationNo;
@Schema(description = "原库位id", example = "8838")
@ExcelProperty("原库位id")
private Long fromLocationId;
@Schema(description = "搬去的目标库位编号")
@ExcelProperty("搬去的目标库位编号")
private String toLocationNo;
@Schema(description = "搬去的目标库位id/停车点/充电点/移动终点", example = "19168")
@ExcelProperty("搬去的目标库位id/停车点/充电点/移动终点")
private Long toLocationId;
@Schema(description = "取货线库id")
@ExcelProperty("取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id(目前设计不会用到此字段)")
@ExcelProperty("放货线库id(目前设计不会用到此字段)")
private Long toLaneId;
@Schema(description = "任务状态(0:未开始、1执行中、2已完成、3已取消、4异常)", example = "1")
@ExcelProperty("任务状态(0:未开始、1执行中、2已完成、3已取消、4异常)")
private Integer taskStatus;
@Schema(description = "优先级")
@ExcelProperty("优先级")
private Long priority;
@Schema(description = "AGV编号")
@ExcelProperty("AGV编号")
private String robotNo;
@Schema(description = "任务类型1自动移库、 ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("任务类型1自动移库、 ")
private Integer taskType;
@Schema(description = "移动类型1去、 2", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@ExcelProperty("移动类型1去、 2")
private Integer moveType;
@Schema(description = "开始时间")
@ExcelProperty("开始时间")
private LocalDateTime startTime;
@Schema(description = "结束时间")
@ExcelProperty("结束时间")
private LocalDateTime endTime;
@Schema(description = "robot_task_auto_move表id来回搬运需要记录上一次搬运的源库位", example = "32231")
@ExcelProperty("robot_task_auto_move表id来回搬运需要记录上一次搬运的源库位")
private Long taskAutoMoveId;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.system.controller.admin.robot.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 机器人自动移库新增/修改 Request VO")
@Data
public class RobotTaskAutoMoveSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30110")
private Long id;
@Schema(description = "机器人任务主表id", example = "24553")
private Long robotTaskId;
@Schema(description = "机器人任务明细表id", example = "11392")
private Long robotTaskDetailId;
@Schema(description = "原库位编号")
private String fromLocationNo;
@Schema(description = "原库位id", example = "8838")
private Long fromLocationId;
@Schema(description = "搬去的目标库位编号")
private String toLocationNo;
@Schema(description = "取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id(目前设计不会用到此字段)")
private Long toLaneId;
@Schema(description = "搬去的目标库位id/停车点/充电点/移动终点", example = "19168")
private Long toLocationId;
@Schema(description = "任务状态(0:未开始、1执行中、2已完成、3已取消、4异常)", example = "1")
private Integer taskStatus;
@Schema(description = "优先级")
private Long priority;
@Schema(description = "AGV编号")
private String robotNo;
@Schema(description = "任务类型1自动移库、 ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "任务类型1自动移库、 )不能为空")
private Integer taskType;
@Schema(description = "移动类型1去、 2", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "移动类型1去、 2不能为空")
private Integer moveType;
@Schema(description = "开始时间")
private LocalDateTime startTime;
@Schema(description = "结束时间")
private LocalDateTime endTime;
@Schema(description = "robot_task_auto_move表id来回搬运需要记录上一次搬运的源库位", example = "32231")
private Long taskAutoMoveId;
}

View File

@ -37,6 +37,15 @@ public class RobotTaskDetailAddVO {
@Schema(description = "所选车辆电量(充电模式)")
private Integer electricity;
@Schema(description = "取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id")
private Long toLaneId;
@Schema(description = "取货库位的排序的位置,越大越优先堆放")
private Long locationNumber;
@Schema(description = "计算后的来源库位编号(前端不用传此字段)")
private String fromLocationNo;
@Schema(description = "计算后的来源库位id(前端不用传此字段)")

View File

@ -46,6 +46,14 @@ public class RobotTaskDetailPageReqVO extends PageParam {
@Schema(description = "AGV动作")
private String robotAction;
@Schema(description = "取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id")
private Long toLaneId;
@Schema(description = "取货库位的排序的位置,越大越优先堆放")
private Long locationNumber;
@Schema(description = "任务状态(0:未开始、1执行中、2已完成、3已取消)", example = "1")
private Integer taskStatus;

View File

@ -78,6 +78,18 @@ public class RobotTaskDetailRespVO {
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "取货线库id")
@ExcelProperty("取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id")
@ExcelProperty("放货线库id")
private Long toLaneId;
@Schema(description = "取货库位的排序的位置,越大越优先堆放")
@ExcelProperty("取货库位的排序的位置,越大越优先堆放")
private Long locationNumber;
@Schema(description = "取货层数")
@ExcelProperty("取货层数")
private Integer fromLocationStorey;

View File

@ -38,6 +38,15 @@ public class RobotTaskDetailSaveReqVO {
@NotNull(message = "取货库位/线库/区域不能为空")
private Long takeId;
@Schema(description = "取货线库id")
private Long fromLaneId;
@Schema(description = "放货线库id")
private Long toLaneId;
@Schema(description = "取货库位的排序的位置,越大越优先堆放")
private Long locationNumber;
@Schema(description = "计算后的来源库位编号")
private String fromLocationNo;
@Schema(description = "计算后的来源库位id")

View File

@ -0,0 +1,98 @@
package cn.iocoder.yudao.module.system.dal.dataobject.robot;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 机器人自动移库 DO
*
* @author 陈宾顺
*/
@TableName("robot_task_auto_move")
@KeySequence("robot_task_auto_move_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RobotTaskAutoMoveDO extends BaseDO {
/**
* 主键ID
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 机器人任务主表id
*/
private Long robotTaskId;
/**
* 机器人任务明细表id
*/
private Long robotTaskDetailId;
/**
* 原库位编号
*/
private String fromLocationNo;
/**
* 原库位id
*/
private Long fromLocationId;
/**
* 搬去的目标库位编号
*/
private String toLocationNo;
/**
* 搬去的目标库位id/停车点/充电点/移动终点
*/
private Long toLocationId;
/**
* 取货线库id
*/
private Long fromLaneId;
/**
* 放货线库id (目前设计不会用到此字段)
*/
private Long toLaneId;
/**
* 任务状态(0:未开始1执行中2已完成3已取消4异常)
*/
private Integer taskStatus;
/**
* 优先级
*/
private Long priority;
/**
* AGV编号
*/
private String robotNo;
/**
* 任务类型1自动移库
*/
private Integer taskType;
/**
* 移动类型1 2
*/
private Integer moveType;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* robot_task_auto_move表id来回搬运需要记录上一次搬运的源库位
*/
private Long taskAutoMoveId;
}

View File

@ -59,6 +59,18 @@ public class RobotTaskDetailDO extends BaseDO {
* 计算后的来源库位id
*/
private Long fromLocationId;
/**
* 取货线库id
*/
private Long fromLaneId;
/**
* 放货线库id
*/
private Long toLaneId;
/**
* 取货库位的排序的位置,越大越优先堆放
*/
private Long locationNumber;
/**
* 计算后的目标库位编号
*/

View File

@ -73,14 +73,6 @@ public interface WareHouseLocationMapper extends BaseMapperX<WareHouseLocationDO
*/
WareHouseLocationDO queryAllByLimit(WareHouseLocationDO wareHouseLocationDO);
/**
* 更新库位锁定状态
* @param id
* @param locationLock
*/
void updateLocationLockStatus(@Param("id") Long id,
@Param("locationLock") int locationLock,
@Param("taskId") Long taskId);
/**
* 查询库位
@ -102,4 +94,18 @@ public interface WareHouseLocationMapper extends BaseMapperX<WareHouseLocationDO
* @return
*/
List<WareHouseLocationRespVO> getLocationByName(WareHouseLocationVO requestVO);
/**
* 查询相通的线库 有货的 排序的位置较小的库位
* @param locations
* @return
*/
List<WareHouseLocationDO> selectLocationByList(@Param("locations") List<WareHouseLocationDO> locations);
/**
* 查询需要移库的库位
* @param locationIds
* @return
*/
List<WareHouseLocationDO> selectNeedMoveLocation(@Param("locationIds") Set<Long> locationIds);
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.system.dal.mysql.robot;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMovePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskAutoMoveDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 机器人自动移库 Mapper
*
* @author 陈宾顺
*/
@Mapper
public interface RobotTaskAutoMoveMapper extends BaseMapperX<RobotTaskAutoMoveDO> {
default PageResult<RobotTaskAutoMoveDO> selectPage(RobotTaskAutoMovePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<RobotTaskAutoMoveDO>()
.eqIfPresent(RobotTaskAutoMoveDO::getRobotTaskId, reqVO.getRobotTaskId())
.eqIfPresent(RobotTaskAutoMoveDO::getRobotTaskDetailId, reqVO.getRobotTaskDetailId())
.eqIfPresent(RobotTaskAutoMoveDO::getFromLocationNo, reqVO.getFromLocationNo())
.eqIfPresent(RobotTaskAutoMoveDO::getFromLocationId, reqVO.getFromLocationId())
.eqIfPresent(RobotTaskAutoMoveDO::getToLocationNo, reqVO.getToLocationNo())
.eqIfPresent(RobotTaskAutoMoveDO::getToLocationId, reqVO.getToLocationId())
.eqIfPresent(RobotTaskAutoMoveDO::getTaskStatus, reqVO.getTaskStatus())
.eqIfPresent(RobotTaskAutoMoveDO::getPriority, reqVO.getPriority())
.eqIfPresent(RobotTaskAutoMoveDO::getRobotNo, reqVO.getRobotNo())
.eqIfPresent(RobotTaskAutoMoveDO::getTaskType, reqVO.getTaskType())
.eqIfPresent(RobotTaskAutoMoveDO::getMoveType, reqVO.getMoveType())
.betweenIfPresent(RobotTaskAutoMoveDO::getStartTime, reqVO.getStartTime())
.betweenIfPresent(RobotTaskAutoMoveDO::getEndTime, reqVO.getEndTime())
.eqIfPresent(RobotTaskAutoMoveDO::getTaskAutoMoveId, reqVO.getTaskAutoMoveId())
.betweenIfPresent(RobotTaskAutoMoveDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(RobotTaskAutoMoveDO::getId));
}
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task_auto_move的task_status
*/
@Getter
@AllArgsConstructor
public enum AutoMoveTaskStatusEnum {
NEW(0),//未开始
DOING(1),//执行中
DONE(2),//已完成
CLOSE(3), //已取消
Exc(4); //异常
/**
* 类型
*/
private final Integer type;
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task_auto_move的task_type
*/
@Getter
@AllArgsConstructor
public enum AutoMoveTaskTypeEnum {
AUTO_MOVE(1); //自动移库
/**
* 类型
*/
private final Integer type;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task_auto_move的move_type
*/
@Getter
@AllArgsConstructor
public enum AutoMoveTypeEnum {
GO(1), //
BACK(1); //
/**
* 类型
*/
private final Integer type;
}

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 下发给车机的类型
*/
@Getter
@AllArgsConstructor
public enum CommandTypeEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task的do_cycle
*/
@Getter
@AllArgsConstructor
public enum DoCycleEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* ware_house_location的location_enable
*/
@Getter
@AllArgsConstructor
public enum LocationEnableEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* ware_house_location的location_lock
*/
@Getter
@AllArgsConstructor
public enum LocationLockEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* ware_house_location的location_use_status
*/
@Getter
@AllArgsConstructor
public enum LocationUseStatusEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task的montage_task
*/
@Getter
@AllArgsConstructor
public enum MontageTaskEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task的do_move_all
*/
@Getter
@AllArgsConstructor
public enum MoveAllEnum {

View File

@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 放货类型
* 放货类型robot_task_detail的release_type
*/
@Getter
@AllArgsConstructor

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task_auto_move表的task_status状态
*/
@Getter
@AllArgsConstructor
public enum RobotMoveStatusEnum {
NEW(0),//未开始
DOING(1),//执行中
DONE(2),//已完成
CLOSE(3), //已取消
Exc(4); //异常
/**
* 类型
*/
private final Integer type;
}

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task_detail的task_status
*/
@Getter
@AllArgsConstructor
public enum RobotTaskDetailStatusEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_information的robot_task_model
*/
@Getter
@AllArgsConstructor
public enum RobotTaskModelEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task表的task_status
*/
@Getter
@AllArgsConstructor
public enum RobotTaskStatusEnum {

View File

@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.system.enums.robot;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* robot_task_detail的task_type
*/
@Getter
@AllArgsConstructor
public enum RobotTaskTypeEnum {

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.job.robot;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.system.enums.redis.RobotCacheLockEnum;
import cn.iocoder.yudao.module.system.service.robot.job.DistributeTasksService;
import cn.iocoder.yudao.module.system.service.robot.job.RobotTaskAutoMoveService;
import cn.iocoder.yudao.module.system.util.redis.RedissonUtils;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
@ -12,6 +13,12 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.REDISSON_NOT_OBTAIN_LOCK;
@Component
@Slf4j
@ -23,6 +30,9 @@ public class RobotJob {
@Autowired
private DistributeTasksService distributeTasksService;
@Autowired
private RobotTaskAutoMoveService robotTaskAutoMoveService;
//下发任务给车机
@XxlJob("DistributeTasksJob")
@TenantJob
@ -37,6 +47,32 @@ public class RobotJob {
} finally {
lock.unlock();
}
}else {
log.info("下发任务未获取到锁");
throw exception(REDISSON_NOT_OBTAIN_LOCK);
}
}
//下发自动移库任务
@XxlJob("DistributeAutoMoveJob")
@TenantJob
public void distributeAutoMoveJob() throws InterruptedException {
log.info("----下发自动移库任务----");
Set<String> locks = new HashSet<>();
locks.add(RobotCacheLockEnum.ROBOT_TASK_DISTRIBUTE_LOCK.getKey());
locks.add(RobotCacheLockEnum.ROBOT_TASK_ADD_LOCK.getKey());
RLock lock = redissonUtils.getLocks(locks);
if (lock.tryLock()){
try {
robotTaskAutoMoveService.distributeAutoMoveJob();
} catch (Exception e) {
log.error("下发自动移库任务出现异常 :{}",e.getMessage());
} finally {
lock.unlock();
}
}else {
log.info("下发自动移库任务未获取到锁");
throw exception(REDISSON_NOT_OBTAIN_LOCK);
}
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.robot;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseLocationDO;
@ -93,7 +94,7 @@ public class RobotTaskServiceImpl implements RobotTaskService {
RLock lock = redissonUtils.getLock(RobotCacheLockEnum.ROBOT_TASK_ADD_LOCK.getKey());
String addResult = "";
if (lock.tryLock(30l,TimeUnit.MINUTES)){
if (lock.tryLock(60l,TimeUnit.MINUTES)){
try {
addResult = addTask(createReqVO);
} catch (Exception e) {
@ -155,6 +156,14 @@ public class RobotTaskServiceImpl implements RobotTaskService {
throw exception0(TASK_CHECK_EXCEPTION.getCode(), "以下机器人已被禁用 ",robotNo);
}
}
//校验任务号是否重复
if (ObjectUtil.isNotEmpty(createReqVO.getTaskNo())) {
List<RobotTaskDO> taskDOS = taskMapper.selectList(new LambdaQueryWrapperX<RobotTaskDO>()
.eq(RobotTaskDO::getTaskNo, createReqVO.getTaskNo()));
if (ObjectUtil.isNotEmpty(taskDOS)) {
throw exception(TASK_CHECK_EXIST_NO);
}
}
}
/**
@ -306,6 +315,9 @@ public class RobotTaskServiceImpl implements RobotTaskService {
robotTaskDetailAddVo.setFromLocationId(stockList.get(i).getId());
robotTaskDetailAddVo.setToLocationNo(releaseStockList.get(i).getLocationNo());
robotTaskDetailAddVo.setToLocationId(releaseStockList.get(i).getId());
robotTaskDetailAddVo.setFromLaneId(stockList.get(i).getLaneId());
robotTaskDetailAddVo.setToLaneId(releaseStockList.get(i).getLaneId());
robotTaskDetailAddVo.setLocationNumber(stockList.get(i).getLocationNumber());
robotTaskDetailAddVo.setRobotTaskId(taskId);
robotTaskDetailAddVo.setPriority(robotTaskVo.getPriority());
robotTaskDetailAddVo.setFromLocationStorey(stockList.get(i).getLocationStorey());
@ -399,7 +411,11 @@ public class RobotTaskServiceImpl implements RobotTaskService {
*/
@Transactional(rollbackFor = Exception.class)
public void doTake(RobotTaskDetailAddVO robotTaskVo, List<Long> locationIds) {
locationMapper.updateLocationLockStatus(robotTaskVo.getFromLocationId(),0,robotTaskVo.getRobotTaskId());
Set<Long> mapIds = new HashSet<>();
if (ObjectUtil.isNotEmpty(robotTaskVo.getRobotNo())) {
mapIds = getMapIdsByRobotNo(robotTaskVo.getRobotNo());
}
setFromLocation(robotTaskVo,mapIds,locationIds);
locationIds.add(robotTaskVo.getFromLocationId());
}
@ -461,6 +477,7 @@ public class RobotTaskServiceImpl implements RobotTaskService {
robotTaskVo.setToLocationNo(locationDO.getLocationNo());
robotTaskVo.setToLocationId(robotTaskVo.getReleaseId());
robotTaskVo.setToLocationStorey(locationDO.getLocationStorey());
robotTaskVo.setToLaneId(locationDO.getLaneId());
if (ObjectUtil.isNotEmpty(mapIds) && !mapIds.contains(locationDO.getMapId())) {
log.error("机器人不能在此放货库位放货 :{}, 机器人编号 :{}", locationDO.getLocationNo(),robotTaskVo.getRobotNo());
throw new RuntimeException("机器人不能在此放货库位放货 "+ robotTaskVo.getReleaseId());
@ -476,6 +493,7 @@ public class RobotTaskServiceImpl implements RobotTaskService {
robotTaskVo.setToLocationNo(wareHouseLocationDO.getLocationNo());
robotTaskVo.setToLocationId(wareHouseLocationDO.getId());
robotTaskVo.setToLocationStorey(wareHouseLocationDO.getLocationStorey());
robotTaskVo.setToLaneId(wareHouseLocationDO.getLaneId());
}
}
@ -493,6 +511,8 @@ public class RobotTaskServiceImpl implements RobotTaskService {
robotTaskVo.setFromLocationNo(locationDO.getLocationNo());
robotTaskVo.setFromLocationId(robotTaskVo.getTakeId());
robotTaskVo.setFromLocationStorey(locationDO.getLocationStorey());
robotTaskVo.setFromLaneId(locationDO.getLaneId());
robotTaskVo.setLocationNumber(locationDO.getLocationNumber());
if (ObjectUtil.isNotEmpty(mapIds) && !mapIds.contains(locationDO.getMapId())) {
log.error("机器人不能在此取货库位取货 :{}, 机器人编号 :{}", locationDO.getLocationNo(),robotTaskVo.getRobotNo());
throw new RuntimeException("机器人不能在此取货库位取货 "+ robotTaskVo.getTakeId());
@ -508,6 +528,8 @@ public class RobotTaskServiceImpl implements RobotTaskService {
robotTaskVo.setFromLocationNo(wareHouseLocationDO.getLocationNo());
robotTaskVo.setFromLocationId(wareHouseLocationDO.getId());
robotTaskVo.setFromLocationStorey(wareHouseLocationDO.getLocationStorey());
robotTaskVo.setFromLaneId(wareHouseLocationDO.getLaneId());
robotTaskVo.setLocationNumber(wareHouseLocationDO.getLocationNumber());
}
}

View File

@ -1,5 +1,19 @@
package cn.iocoder.yudao.module.system.service.robot.job;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDetailDO;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
public interface DistributeTasksService {
/**
* 下发搬运任务
*/
void distributeTasks();
/**
* 获取待执行的机器人和任务
*/
Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> getRobotAndTaskDetails();
}

View File

@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskMapper;
import cn.iocoder.yudao.module.system.enums.robot.*;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@ -75,6 +76,29 @@ public class DistributeTasksServiceImpl implements DistributeTasksService {
@Override
@Transactional(rollbackFor = Exception.class)
public void distributeTasks() {
Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> robotAndTaskDetails = getRobotAndTaskDetails();
List<RobotInformationDO> robots = robotAndTaskDetails.getLeft();
if (ObjectUtil.isEmpty(robots)) {
return;
}
List<RobotTaskDetailDO> taskDetailDOS = robotAndTaskDetails.getRight();
if (ObjectUtil.isEmpty(taskDetailDOS)) {
return;
}
//任务下发给机器人
distributeTasks(robots,taskDetailDOS);
}
/**
* 获取待执行的机器人和任务
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> getRobotAndTaskDetails() {
Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> pair = Pair.of(new ArrayList<>(), new ArrayList<>());;
TenantContextHolder.setTenantId(1L);
List<RobotInformationDO> robots = robotInformationMapper.selectList(new LambdaQueryWrapperX<RobotInformationDO>()
.eq(RobotInformationDO::getRobotStatus, (RobotStatusEnum.STAND_BY.getType()))
@ -83,9 +107,11 @@ public class DistributeTasksServiceImpl implements DistributeTasksService {
if (robots.isEmpty()) {
log.info("暂无空闲的机器人");
return;
return pair;
}
//todo 还需要根据车机上报的信息过滤掉不能用的机器人
//拼接任务id
List<Long> detailDongIds = robotTaskDetailMapper.getDoingTaskIds();
List<Long> montageTaskIds = robotTaskMapper.getUnDoAndDoingTaskIds(MontageTaskEnum.YES.getType(),detailDongIds);
@ -94,24 +120,16 @@ public class DistributeTasksServiceImpl implements DistributeTasksService {
if (ObjectUtil.isEmpty(montageTaskIds) && ObjectUtil.isEmpty(singleTaskIds)) {
log.info("暂无需要处理的主任务");
return;
return pair;
}
List<RobotTaskDetailDO> taskDetailDOS = getTaskDetail(montageTaskIds,singleTaskIds);
if (taskDetailDOS.isEmpty()) {
log.info("暂无需要处理的明细任务");
return;
return pair;
}
//如果系统暂停 则不下发任务
//todo 更新数据库顺序 后续改为和创建订单一致
// todo 更新数据库 改为批量修改
//任务下发给机器人
distributeTasks(robots,taskDetailDOS);
return ImmutablePair.of(robots, taskDetailDOS);
}
/**
@ -395,6 +413,8 @@ public class DistributeTasksServiceImpl implements DistributeTasksService {
return;
}
// todo 需要校验取放线库 都没有移库的任务
RobotAcceptTaskDTO robotTaskDO = new RobotAcceptTaskDTO();
robotTaskDO.setOrder_id(taskDetailDO.getId().toString());
robotTaskDO.setOrder_type("出库");//未定
@ -583,7 +603,7 @@ public class DistributeTasksServiceImpl implements DistributeTasksService {
taskIdSet.add(taskDetailDO.getRobotTaskId());
}
private Pair<String, String> getMadAddressRobotNo(RobotTaskDetailDO taskDetailDO, List<RobotInformationDO> robots,
public Pair<String, String> getMadAddressRobotNo(RobotTaskDetailDO taskDetailDO, List<RobotInformationDO> robots,
WareHouseLocationDO fromLocation, WareHouseLocationDO toLocation) {
String macAddress = "";
String robotNo = taskDetailDO.getRobotNo();

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.system.service.robot.job;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMovePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMoveSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskAutoMoveDO;
/**
* 机器人自动移库 Service 接口
*
* @author 陈宾顺
*/
public interface RobotTaskAutoMoveService {
/**
* 创建机器人自动移库
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createTaskAutoMove(@Valid RobotTaskAutoMoveSaveReqVO createReqVO);
/**
* 更新机器人自动移库
*
* @param updateReqVO 更新信息
*/
void updateTaskAutoMove(@Valid RobotTaskAutoMoveSaveReqVO updateReqVO);
/**
* 删除机器人自动移库
*
* @param id 编号
*/
void deleteTaskAutoMove(Long id);
/**
* 获得机器人自动移库
*
* @param id 编号
* @return 机器人自动移库
*/
RobotTaskAutoMoveDO getTaskAutoMove(Long id);
/**
* 获得机器人自动移库分页
*
* @param pageReqVO 分页查询
* @return 机器人自动移库分页
*/
PageResult<RobotTaskAutoMoveDO> getTaskAutoMovePage(RobotTaskAutoMovePageReqVO pageReqVO);
/**
* 自动移库
*/
void distributeAutoMoveJob();
}

View File

@ -0,0 +1,412 @@
package cn.iocoder.yudao.module.system.service.robot.job;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.mqtt.api.task.RobotTaskApi;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMovePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.robot.vo.RobotTaskAutoMoveSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.houselocation.WareHouseLocationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotInformationDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskAutoMoveDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotTaskDetailDO;
import cn.iocoder.yudao.module.system.dal.mysql.houselocation.WareHouseLocationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.positionmap.PositionMapItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotInformationMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskAutoMoveMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskDetailMapper;
import cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskMapper;
import cn.iocoder.yudao.module.system.enums.robot.*;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.TASK_AUTO_MOVE_NOT_EXISTS;
/**
* 机器人自动移库 Service 实现类
*
* @author 陈宾顺
*/
@Service
@Validated
@Slf4j
public class RobotTaskAutoMoveServiceImpl implements RobotTaskAutoMoveService {
@Resource
private RobotTaskAutoMoveMapper taskAutoMoveMapper;
@Autowired
private RobotInformationMapper robotInformationMapper;
@Autowired
private RobotTaskMapper robotTaskMapper;
@Autowired
private RobotTaskDetailMapper robotTaskDetailMapper;
@Resource
private RobotTaskApi robotTaskApi;
@Resource
private PositionMapItemMapper positionMapItemMapper;
@Resource
private WareHouseLocationMapper locationMapper;
@Autowired
private DistributeTasksService distributeTasksService;
@Value("${zn.lane_auto_move:true}")
private Boolean laneAutoMove;
@Override
public Long createTaskAutoMove(RobotTaskAutoMoveSaveReqVO createReqVO) {
// 插入
RobotTaskAutoMoveDO taskAutoMove = BeanUtils.toBean(createReqVO, RobotTaskAutoMoveDO.class);
taskAutoMoveMapper.insert(taskAutoMove);
// 返回
return taskAutoMove.getId();
}
@Override
public void updateTaskAutoMove(RobotTaskAutoMoveSaveReqVO updateReqVO) {
// 校验存在
validateTaskAutoMoveExists(updateReqVO.getId());
// 更新
RobotTaskAutoMoveDO updateObj = BeanUtils.toBean(updateReqVO, RobotTaskAutoMoveDO.class);
taskAutoMoveMapper.updateById(updateObj);
}
@Override
public void deleteTaskAutoMove(Long id) {
// 校验存在
validateTaskAutoMoveExists(id);
// 删除
taskAutoMoveMapper.deleteById(id);
}
private void validateTaskAutoMoveExists(Long id) {
if (taskAutoMoveMapper.selectById(id) == null) {
throw exception(TASK_AUTO_MOVE_NOT_EXISTS);
}
}
@Override
public RobotTaskAutoMoveDO getTaskAutoMove(Long id) {
return taskAutoMoveMapper.selectById(id);
}
@Override
public PageResult<RobotTaskAutoMoveDO> getTaskAutoMovePage(RobotTaskAutoMovePageReqVO pageReqVO) {
return taskAutoMoveMapper.selectPage(pageReqVO);
}
/**
* 自动移库
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void distributeAutoMoveJob() {
TenantContextHolder.setTenantId(1L);
Pair<List<RobotInformationDO>, List<RobotTaskDetailDO>> robotAndTaskDetails =
distributeTasksService.getRobotAndTaskDetails();
List<RobotInformationDO> robots = robotAndTaskDetails.getLeft();
if (ObjectUtil.isEmpty(robots)) {
log.info("没有空闲的机器人能执行移库任务");
return;
}
List<RobotTaskDetailDO> taskDetailDOS = robotAndTaskDetails.getRight();
if (ObjectUtil.isEmpty(taskDetailDOS)) {
log.info("没有需要自动移库的明细任务");
return;
}
//查询需要移库的普通库位(非线库)
List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> ordinaryPairs = getOrdinaryAutoMoveLocation(taskDetailDOS);
//查询需要移库的库位(线库)
List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> pairs = getLaneAutoMoveLocation(taskDetailDOS);
if (ObjectUtil.isEmpty(ordinaryPairs) && ObjectUtil.isEmpty(pairs)) {
log.info("查不到需要移库的库位");
return;
}
ordinaryPairs.addAll(pairs);
//下发任务
List<RobotTaskAutoMoveDO> robotTaskAutoMoveDOS = allocationRobot(robots, ordinaryPairs);
if (ObjectUtil.isEmpty(robotTaskAutoMoveDOS)) {
log.info("暂无需要下发移库的任务");
return;
}
taskAutoMoveMapper.insert(robotTaskAutoMoveDOS);
Set<String> robotNos = robotTaskAutoMoveDOS.stream().map(RobotTaskAutoMoveDO::getRobotNo).collect(Collectors.toSet());
List<RobotInformationDO> robotInformationDOS = robotInformationMapper.selectByRobotNos(robotNos);
robotInformationDOS.stream().forEach(robotInformationDO -> {
robotInformationDO.setRobotStatus(RobotStatusEnum.DOING.getType());
});
robotInformationMapper.updateBatch(robotInformationDOS);
List<Long> locationIds = new ArrayList<>();
robotTaskAutoMoveDOS.forEach(robotTaskAutoMoveDO -> {
locationIds.add(robotTaskAutoMoveDO.getFromLocationId());
locationIds.add(robotTaskAutoMoveDO.getToLocationId());
});
List<List<Long>> fromPartition = Lists.partition(locationIds, 100);
fromPartition.forEach(list-> {
locationMapper.updateLocationLockList(list,0l,LocationLockEnum.NO.getType());
});
// todo 搬运任务下发给机器人
}
/**
* 查询需要移库的普通库位(非线库)
* @param taskDetailDOS
* @return
*/
private List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> getOrdinaryAutoMoveLocation(List<RobotTaskDetailDO> taskDetailDOS) {
List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> result = new ArrayList<>();
List<RobotTaskDetailDO> details = taskDetailDOS.stream().filter(v ->
(RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(v.getTaskType())
|| RobotTaskTypeEnum.TAKE.getType().equals(v.getTaskType())
|| RobotTaskTypeEnum.RELEASE.getType().equals(v.getTaskType()))
&& ObjectUtil.isEmpty(v.getFromLaneId()))
.collect(Collectors.toList());
if (ObjectUtil.isEmpty(details)) {
log.info("普通库位, 不需要执行移库");
return result;
}
for (RobotTaskDetailDO robotTaskDetailDO : details) {
Set<Long> doingLocationIds = new HashSet<>();
if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLocationId())) {
doingLocationIds.add(robotTaskDetailDO.getFromLocationId());
}
if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getToLocationId())) {
doingLocationIds.add(robotTaskDetailDO.getToLocationId());
}
if (ObjectUtil.isEmpty(doingLocationIds)) {
continue;
}
List<WareHouseLocationDO> locationDOS = locationMapper.selectNeedMoveLocation(doingLocationIds);
if (ObjectUtil.isEmpty(locationDOS)) {
log.info("暂无需要自动移库的库位 :{}" ,robotTaskDetailDO);
continue;
}
boolean enableOrLock = locationDOS.stream().anyMatch(v -> LocationEnableEnum.NO.getType().equals(v.getLocationEnable())
|| LocationLockEnum.NO.getType().equals(v.getLocationLock()));
if (enableOrLock) {
log.info("此库位被锁定或者禁用,无法执行移库 :{}",locationDOS);
//todo 后期加到告警表里面 方便排查问题
continue;
}
Pair<RobotTaskDetailDO, List<WareHouseLocationDO>> pair = Pair.of(robotTaskDetailDO, locationDOS);
result.add(pair);
}
return result;
}
/**
* 下发任务
* @param robots
* @param pairs
*/
@Transactional(rollbackFor = Exception.class)
public List<RobotTaskAutoMoveDO> allocationRobot(List<RobotInformationDO> robots, List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> pairs) {
List<RobotTaskAutoMoveDO> list = new ArrayList<>();
if (ObjectUtil.isEmpty(robots) || ObjectUtil.isEmpty(pairs)) {
return list;
}
//查找空库位
// todo 并且 map_item_id没有处理中的任务
List<WareHouseLocationDO> emptyLocations = locationMapper.selectList(new LambdaQueryWrapperX<WareHouseLocationDO>()
.isNull(WareHouseLocationDO::getLaneId)
.eq(WareHouseLocationDO::getLocationUseStatus, LocationUseStatusEnum.NO.getType())
.eq(WareHouseLocationDO::getLocationEnable, LocationEnableEnum.YES.getType())
.eq(WareHouseLocationDO::getLocationLock, LocationLockEnum.YES.getType())
.orderByDesc(WareHouseLocationDO::getLocationNumber));
if (ObjectUtil.isEmpty(emptyLocations)) {
log.info("查不到空库位,无法执行移库任务");
return list;
}
for (Pair<RobotTaskDetailDO, List<WareHouseLocationDO>> pair : pairs) {
RobotTaskDetailDO detailDO = pair.getLeft();
for (WareHouseLocationDO wareHouseLocationDO : pair.getRight()) {
if (ObjectUtil.isEmpty(robots) || ObjectUtil.isEmpty(emptyLocations)) {
return list;
}
// 校验机器人工作区域
RobotInformationDO robotInformationDO = robots.stream()
.filter(v ->
ObjectUtil.isNotEmpty(v.getMacAddress())
&& v.getFloorAreaJson().contains(wareHouseLocationDO.getAreaId())
&& v.getFloorAreaJson().contains(emptyLocations.get(0).getAreaId()))
.findFirst()
.orElse(new RobotInformationDO());
if (ObjectUtil.isEmpty(robotInformationDO) || ObjectUtil.isEmpty(robotInformationDO.getRobotNo())) {
continue;
}
RobotTaskAutoMoveDO autoMoveDO = new RobotTaskAutoMoveDO();
autoMoveDO.setRobotTaskDetailId(detailDO.getId());
autoMoveDO.setRobotTaskId(detailDO.getRobotTaskId());
autoMoveDO.setFromLocationId(wareHouseLocationDO.getId());
autoMoveDO.setFromLocationNo(wareHouseLocationDO.getLocationNo());
autoMoveDO.setToLocationId(emptyLocations.get(0).getId());
autoMoveDO.setToLocationNo(emptyLocations.get(0).getLocationNo());
autoMoveDO.setFromLaneId(wareHouseLocationDO.getLaneId());
autoMoveDO.setTaskStatus(AutoMoveTaskStatusEnum.DOING.getType());
autoMoveDO.setPriority(wareHouseLocationDO.getLocationNumber());
autoMoveDO.setRobotNo(robotInformationDO.getRobotNo());
autoMoveDO.setTaskType(AutoMoveTaskTypeEnum.AUTO_MOVE.getType());
autoMoveDO.setMoveType(AutoMoveTypeEnum.GO.getType());
autoMoveDO.setStartTime(LocalDateTime.now());
list.add(autoMoveDO);
robots.removeIf(v -> v.getRobotNo().equals(autoMoveDO.getRobotNo()));
emptyLocations.removeIf(v -> v.getId().equals(autoMoveDO.getToLocationId()));
}
}
return list;
}
/**
* 查询需要移库的库位(线库)
* @param taskDetailDOS
* @return
*/
public List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> getLaneAutoMoveLocation(List<RobotTaskDetailDO> taskDetailDOS) {
List<Pair<RobotTaskDetailDO, List<WareHouseLocationDO>>> result = new ArrayList<>();
if (ObjectUtil.isEmpty(taskDetailDOS) || !laneAutoMove) {
log.info("没有线库的任务明细, 不需要执行线库移库");
return result;
}
//此线库不能有移动过来的任务
List<RobotTaskDetailDO> doingTaskDetail = robotTaskDetailMapper.selectList(new LambdaQueryWrapperX<RobotTaskDetailDO>()
.eq(RobotTaskDetailDO::getTaskStatus, (RobotTaskStatusEnum.DOING.getType()))
.in(RobotTaskDetailDO::getTaskType, RobotTaskTypeEnum.TAKE_RELEASE.getType(), RobotTaskTypeEnum.TAKE.getType()));
Set<Long> doingTaskDetailLaneIds = new HashSet<>();
if (ObjectUtil.isNotEmpty(doingTaskDetail)) {
for (RobotTaskDetailDO robotTaskDetailDO : doingTaskDetail) {
if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLaneId())) {
doingTaskDetailLaneIds.add(robotTaskDetailDO.getFromLaneId());
}
if (ObjectUtil.isNotEmpty(robotTaskDetailDO.getToLaneId())) {
doingTaskDetailLaneIds.add(robotTaskDetailDO.getToLaneId());
}
}
}
//自动移库的任务
List<RobotTaskAutoMoveDO> robotTaskAutoMoveDOS = taskAutoMoveMapper.selectList(new LambdaQueryWrapperX<RobotTaskAutoMoveDO>()
.eq(RobotTaskAutoMoveDO::getTaskStatus, RobotTaskStatusEnum.DOING.getType()));
Set<Long> doingAutoMoveLaneIds = new HashSet<>();
Set<Long> doingDetailIds = new HashSet<>();
if (ObjectUtil.isNotEmpty(robotTaskAutoMoveDOS)) {
robotTaskAutoMoveDOS.forEach(robotTaskAutoMoveDO -> {
if (ObjectUtil.isNotEmpty(robotTaskAutoMoveDO.getFromLaneId())) {
doingAutoMoveLaneIds.add(robotTaskAutoMoveDO.getFromLaneId());
}
if (ObjectUtil.isNotEmpty(robotTaskAutoMoveDO.getRobotTaskDetailId())) {
doingDetailIds.add(robotTaskAutoMoveDO.getRobotTaskDetailId());
}
});
}
//只有库位为线库且任务类型为取放货和仅取货的需要移库
List<RobotTaskDetailDO> details = taskDetailDOS.stream().filter(v ->
(RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(v.getTaskType())
|| RobotTaskTypeEnum.TAKE.getType().equals(v.getTaskType()))
&& ObjectUtil.isNotEmpty(v.getFromLaneId())
&& (/*ObjectUtil.isNotEmpty(doingAutoMoveLaneIds) &&*/ !doingAutoMoveLaneIds.contains(v.getFromLaneId()))
&& (/*ObjectUtil.isNotEmpty(doingDetailIds) &&*/ !doingDetailIds.contains(v.getId()))
&& (/*ObjectUtil.isNotEmpty(doingTaskDetailLaneIds) &&*/ !doingTaskDetailLaneIds.contains(v.getFromLaneId())))
.collect(Collectors.toList());
if (ObjectUtil.isEmpty(details)) {
log.info("没有线库的任务, 不需要执行线库移库");
return result;
}
Map<Long, List<RobotTaskDetailDO>> detailMaps =
details.stream().collect(Collectors.groupingBy(RobotTaskDetailDO::getFromLaneId));
//查找每一个线库最小的一个库位
for (Map.Entry<Long, List<RobotTaskDetailDO>> detaiMap : detailMaps.entrySet()) {
List<WareHouseLocationDO> locations = new ArrayList<>();
RobotTaskDetailDO robotTaskDetail = detaiMap.getValue()
.stream()
.min(Comparator.comparing(RobotTaskDetailDO::getLocationNumber)).get();
if (ObjectUtil.isNotEmpty(robotTaskDetail) && ObjectUtil.isNotEmpty(robotTaskDetail.getLocationNumber())) {
WareHouseLocationDO location = WareHouseLocationDO.builder().locationNumber(robotTaskDetail.getLocationNumber())
.locationUseStatus(LocationUseStatusEnum.YES.getType())
.locationLock(LocationLockEnum.YES.getType())
.laneId(detaiMap.getKey()).build();
locations.add(location);
}
log.info("查询是否需要执行移库任务的线库id和对应的排序位置 :{}", JSON.toJSONString(locations));
if (ObjectUtil.isNotEmpty(locations)) {
List<WareHouseLocationDO> wareHouseLocationDOS = locationMapper.selectLocationByList(locations);
if (ObjectUtil.isNotEmpty(wareHouseLocationDOS)) {
Pair<RobotTaskDetailDO, List<WareHouseLocationDO>> pair = Pair.of(robotTaskDetail, wareHouseLocationDOS);
result.add(pair);
}
}
}
return result;
}
}

View File

@ -225,4 +225,5 @@ zn:
scan_height: 0.4 #扫描高度
parm: 5000 #等待时间
lift_height: 0.1 #抬高托盘高度
move_height: 0.1 #行走高度
move_height: 0.1 #行走高度
lane_auto_move: true #线库是否自动移库 true:线库执行自动移库 、false线库关闭执行自动移库

View File

@ -481,20 +481,7 @@
where id = #{id}
</update>
<update id="updateLocationLockStatus">
update
ware_house_location
<set>
<if test="taskId != null">
task_id = #{taskId},
</if>
<if test="locationLock != null">
location_lock = #{locationLock},
</if>
</set>
where
id = #{id}
</update>
<update id="updateLocationLockList">
update
@ -690,4 +677,84 @@
</choose>
</select>
<select id="selectLocationByList" resultMap="BaseResultMap">
select
<include refid="base_sql"></include>
from
ware_house_location
<where>
<foreach collection="locations" item="item" open="(" close=")"
separator=",">
1=1
<if test="item.laneId != null">
and lane_id = #{item.laneId}
</if>
<if test="item.locationNumber != null">
and location_number &lt; #{item.locationNumber}
</if>
<if test="item.locationLock != null">
and location_lock = #{item.locationLock}
</if>
<if test="item.locationUseStatus != null">
and location_use_status = #{item.locationUseStatus}
</if>
<if test="1==1">
and deleted = '0'
</if>
</foreach>
</where>
order by location_number asc
</select>
<select id="selectNeedMoveLocation" resultMap="BaseResultMap">
select
t2.id,
t2.lane_id,
t2.lane_name,
t2.area_id,
t2.area_name,
t2.location_no,
t2.location_yaw,
t2.group_name,
t2.sku_info,
t2.sku_batch,
t2.sku_number,
t2.tray_info,
t2.location_enable,
t2.location_lock,
t2.location_use_status,
t2.location_x,
t2.location_y,
t2.location_wide,
t2.location_deep,
t2.location_height,
t2.location_default_height,
t2.location_total_height,
t2.location_tray_height,
t2.location_storey,
t2.location_type,
t2.location_number,
t2.map_id,
t2.map_item_id
from
ware_house_location t1, ware_house_location t2
WHERE
t1.map_item_id = t2.map_item_id
and t1.id in
<foreach collection="locationIds" item="id" index="index" open="(" close=")"
separator=",">
#{id}
</foreach>
and t2.location_number &lt; t1.location_number
and t2.location_use_status = '1'
and t2.id not in
<foreach collection="locationIds" item="id" index="index" open="(" close=")"
separator=",">
#{id}
</foreach>
order by t2.location_number asc
</select>
</mapper>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.system.dal.mysql.robot.RobotTaskAutoMoveMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -13,6 +13,9 @@
<result property="takeId" column="take_id" jdbcType="INTEGER"/>
<result property="fromLocationNo" column="from_location_no" jdbcType="VARCHAR"/>
<result property="fromLocationId" column="from_location_id" jdbcType="INTEGER"/>
<result property="fromLaneId" column="from_lane_id" jdbcType="INTEGER"/>
<result property="toLaneId" column="to_lane_id" jdbcType="INTEGER"/>
<result property="locationNumber" column="location_number" jdbcType="INTEGER"/>
<result property="toLocationNo" column="to_location_no" jdbcType="VARCHAR"/>
<result property="toLocationId" column="to_location_id" jdbcType="INTEGER"/>
<result property="fromLocationStorey" column="from_location_storey" jdbcType="INTEGER"/>
@ -42,6 +45,9 @@
take_id,
from_location_no,
from_location_id,
from_lane_id,
to_lane_id,
location_number
to_location_no,
to_location_id,
robot_no,
@ -360,13 +366,13 @@
<insert id="insertBatchList" >
insert into robot_task_detail(robot_task_id, task_type, release_type, take_type, release_id, take_id,
from_location_no, from_location_id, to_location_no, to_location_id, robot_no,from_location_storey,
to_location_storey,priority)
to_location_storey,priority,from_lane_id,to_lane_id,location_number)
values
<foreach collection="taskDetailList" item="entity" separator=",">
(#{entity.robotTaskId}, #{entity.taskType}, #{entity.releaseType}, #{entity.takeType}, #{entity.releaseId},
#{entity.takeId}, #{entity.fromLocationNo}, #{entity.fromLocationId}, #{entity.toLocationNo},
#{entity.toLocationId}, #{entity.robotNo}, #{entity.fromLocationStorey}, #{entity.toLocationStorey}
, #{entity.priority})
, #{entity.priority}, #{entity.fromLaneId}, #{entity.toLaneId}, #{entity.locationNumber})
</foreach>
</insert>