|
|
@ -1,31 +1,32 @@ |
|
|
|
package org.nl.b_lms.pda.service.impl; |
|
|
|
|
|
|
|
import cn.hutool.core.util.ObjectUtil; |
|
|
|
import cn.hutool.core.util.StrUtil; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
|
import lombok.SneakyThrows; |
|
|
|
import org.apache.commons.collections4.CollectionUtils; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.nl.b_lms.bst.ivt.boxstack.enums.BoxStackEnum; |
|
|
|
import org.nl.b_lms.bst.ivt.boxstack.service.IBstIvtBoxstackService; |
|
|
|
import org.nl.b_lms.bst.ivt.boxstack.service.dao.BstIvtBoxstack; |
|
|
|
import org.nl.b_lms.pda.service.BoxStackInOutService; |
|
|
|
import org.nl.b_lms.sch.task.dao.SchBaseTask; |
|
|
|
import org.nl.b_lms.sch.task.service.IschBaseTaskService; |
|
|
|
import org.nl.b_lms.sch.tasks.boxstack.MxInTask; |
|
|
|
import org.nl.b_lms.storage_manage.database.service.IBstIvtBoxinfoService; |
|
|
|
import org.nl.b_lms.storage_manage.database.service.dao.BstIvtBoxinfo; |
|
|
|
import org.nl.modules.common.exception.BadRequestException; |
|
|
|
import org.nl.system.service.param.ISysParamService; |
|
|
|
import org.nl.system.service.param.dao.Param; |
|
|
|
import org.nl.wms.sch.service.PointService; |
|
|
|
import org.nl.wms.sch.service.dto.PointDto; |
|
|
|
import org.redisson.api.RLock; |
|
|
|
import org.redisson.api.RedissonClient; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
|
|
|
|
import javax.annotation.Resource; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.List; |
|
|
|
import java.util.*; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description TODO |
|
|
@ -47,90 +48,197 @@ public class BoxStackInOutServiceImpl implements BoxStackInOutService { |
|
|
|
private IschBaseTaskService taskService; |
|
|
|
@Resource |
|
|
|
private IBstIvtBoxstackService boxstackService; |
|
|
|
@Resource |
|
|
|
private ISysParamService paramService; |
|
|
|
|
|
|
|
@SneakyThrows |
|
|
|
@Override |
|
|
|
public void boxIn(JSONObject reqParam) { |
|
|
|
String point_code = reqParam.getString("point_code"); |
|
|
|
if (StringUtils.isBlank(point_code)) { |
|
|
|
String pointCode = reqParam.getString("point_code"); |
|
|
|
if (StringUtils.isBlank(pointCode)) { |
|
|
|
throw new BadRequestException("请输入入库点位"); |
|
|
|
} |
|
|
|
PointDto pointDto = pointService.findByCode(point_code); |
|
|
|
if (ObjectUtil.isEmpty(pointDto)) { |
|
|
|
throw new BadRequestException("入库点位不存在"); |
|
|
|
} |
|
|
|
String vehicle_code = reqParam.getString("vehicle_code"); |
|
|
|
if (StringUtils.isBlank(vehicle_code)) { |
|
|
|
Optional.ofNullable(pointService.findByCode(pointCode)) |
|
|
|
.orElseThrow(() -> new BadRequestException("入库点位不存在")); |
|
|
|
|
|
|
|
String vehicleCodeStr = reqParam.getString("vehicle_code"); |
|
|
|
if (StringUtils.isBlank(vehicleCodeStr)) { |
|
|
|
throw new BadRequestException("请输入木箱号"); |
|
|
|
} |
|
|
|
String[] vehicle_codes = vehicle_code.split(","); |
|
|
|
List<String> box_specs = new ArrayList<>(); |
|
|
|
for (String vehicle_code1 : vehicle_codes) { |
|
|
|
if (StringUtils.isBlank(vehicle_code1)) { |
|
|
|
BstIvtBoxinfo boxinfo = boxinfoService.getOne(new LambdaQueryWrapper<BstIvtBoxinfo>().eq(BstIvtBoxinfo::getBox_no, vehicle_code1)); |
|
|
|
String[] vehicleCodes = vehicleCodeStr.split(","); |
|
|
|
List<String> boxSpecs = new ArrayList<>(); |
|
|
|
|
|
|
|
for (String boxNo : vehicleCodes) { |
|
|
|
if (StringUtils.isNotBlank(boxNo)) { |
|
|
|
BstIvtBoxinfo boxinfo = boxinfoService.getOne( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxinfo>().eq(BstIvtBoxinfo::getBox_no, boxNo)); |
|
|
|
if (ObjectUtil.isEmpty(boxinfo)) { |
|
|
|
throw new BadRequestException("木箱号:" + vehicle_code1 + "不存在"); |
|
|
|
throw new BadRequestException("木箱号: " + boxNo + " 不存在"); |
|
|
|
} |
|
|
|
box_specs.add(boxinfo.getMaterial_code()); |
|
|
|
boxSpecs.add(boxinfo.getMaterial_code()); |
|
|
|
} |
|
|
|
} |
|
|
|
String firstMaterial = box_specs.get(0); |
|
|
|
boolean allMatch = box_specs.stream().allMatch(code -> code.equals(firstMaterial)); |
|
|
|
|
|
|
|
if (boxSpecs.isEmpty()) { |
|
|
|
throw new BadRequestException("未检测到有效木箱号"); |
|
|
|
} |
|
|
|
|
|
|
|
String firstMaterial = boxSpecs.get(0); |
|
|
|
boolean allMatch = boxSpecs.stream().allMatch(code -> code.equals(firstMaterial)); |
|
|
|
if (!allMatch) { |
|
|
|
throw new BadRequestException("木箱规格不一致"); |
|
|
|
} |
|
|
|
|
|
|
|
String boxAreaRelationStr = Optional.ofNullable(paramService.findByCode("box_area_relation")) |
|
|
|
.map(Param::getValue) |
|
|
|
.orElseThrow(() -> new BadRequestException("请配置木箱规格区域对应关系")); |
|
|
|
|
|
|
|
JSONObject boxAreaRelationJSON = JSONObject.parseObject(boxAreaRelationStr); |
|
|
|
String boxArea = boxAreaRelationJSON.getString(firstMaterial); |
|
|
|
if (StringUtils.isBlank(boxArea)) { |
|
|
|
throw new BadRequestException("请配置木箱规格: " + firstMaterial + " 的区域"); |
|
|
|
} |
|
|
|
|
|
|
|
RLock lock = redissonClient.getLock("boxIn"); |
|
|
|
boolean tryLock = lock.tryLock(0, TimeUnit.SECONDS); |
|
|
|
try { |
|
|
|
if (tryLock) { |
|
|
|
//查询第一排非缓存区的并且无货的库位
|
|
|
|
List<BstIvtBoxstack> noHasGoodsPointList = boxstackService.list(new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
if (!tryLock) { |
|
|
|
throw new BadRequestException("系统繁忙,请稍后重试"); |
|
|
|
} |
|
|
|
|
|
|
|
if (StrUtil.equals(boxArea, BoxStackEnum.POINT_STATUS.code("零散区"))) { |
|
|
|
handleLooseAreaIn(pointCode, firstMaterial, vehicleCodeStr); |
|
|
|
} else if (StrUtil.equals(boxArea, BoxStackEnum.POINT_STATUS.code("密集区"))) { |
|
|
|
handleDenseAreaIn(pointCode, firstMaterial, vehicleCodeStr); |
|
|
|
} else { |
|
|
|
throw new BadRequestException("不支持的区域配置: " + boxArea); |
|
|
|
} |
|
|
|
} finally { |
|
|
|
if (lock.isLocked() && lock.isHeldByCurrentThread()) { |
|
|
|
lock.unlock(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void handleLooseAreaIn(String pointCode, String firstMaterial, String vehicleCodeStr) { |
|
|
|
List<BstIvtBoxstack> emptyLoosePoints = boxstackService.list( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.isNull(BstIvtBoxstack::getBox_spec) |
|
|
|
.notIn(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("缓存区")) |
|
|
|
.eq(BstIvtBoxstack::getCurrent_layer_count, 0) |
|
|
|
.eq(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("零散区")) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用")) |
|
|
|
); |
|
|
|
if (CollectionUtils.isEmpty(emptyLoosePoints)) { |
|
|
|
throw new BadRequestException("该木箱规格在零散区未找到可用库位"); |
|
|
|
} |
|
|
|
|
|
|
|
for (BstIvtBoxstack stack : emptyLoosePoints) { |
|
|
|
if (CollectionUtils.isEmpty(taskService.checkHaveTask(stack.getStack_code()))) { |
|
|
|
JSONObject taskParam = buildTaskParam(firstMaterial, vehicleCodeStr, |
|
|
|
BoxStackEnum.AGV_ACTION_TYPE.code("普通任务"), |
|
|
|
BoxStackEnum.TASK_TYPE.code("木箱入库"), |
|
|
|
pointCode, stack.getStack_code()); |
|
|
|
mxInTask.createTask(taskParam); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
throw new BadRequestException("零散区无可用库位可执行任务,请稍后重试"); |
|
|
|
} |
|
|
|
|
|
|
|
private void handleDenseAreaIn(String pointCode, String firstMaterial, String vehicleCodeStr) { |
|
|
|
List<BstIvtBoxstack> firstRowWithMaterial = boxstackService.list( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.eq(BstIvtBoxstack::getBox_spec, firstMaterial) |
|
|
|
.eq(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("密集区")) |
|
|
|
.eq(BstIvtBoxstack::getX, 1) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用"))); |
|
|
|
//判断是否满足最大数量,如果不满足
|
|
|
|
if (noHasGoodsPointList.size() <= 3) { |
|
|
|
//获取相同木箱规格的的第一排的库位
|
|
|
|
List<BstIvtBoxstack> hasGoodsPointList = boxstackService.list(new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.eq(BstIvtBoxstack::getBox_spec, firstMaterial) |
|
|
|
.notIn(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("缓存区")) |
|
|
|
.eq(BstIvtBoxstack::getX, 1) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用"))); |
|
|
|
//遍历相同木箱规格同列不同排的库位是否大于最大数量
|
|
|
|
int currentCanUseCount = 0; |
|
|
|
for (BstIvtBoxstack bstIvtBoxstack : hasGoodsPointList) { |
|
|
|
Integer x = bstIvtBoxstack.getX(); |
|
|
|
Integer y = bstIvtBoxstack.getY(); |
|
|
|
List<BstIvtBoxstack> list = boxstackService.list(new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用")) |
|
|
|
); |
|
|
|
|
|
|
|
if (!CollectionUtils.isEmpty(firstRowWithMaterial)) { |
|
|
|
for (BstIvtBoxstack stack : firstRowWithMaterial) { |
|
|
|
Integer y = stack.getY(); |
|
|
|
|
|
|
|
List<BstIvtBoxstack> emptyPointsInColumn = boxstackService.list( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.isNull(BstIvtBoxstack::getBox_spec) |
|
|
|
.eq(BstIvtBoxstack::getCurrent_layer_count, 0) |
|
|
|
.eq(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("密集区")) |
|
|
|
.eq(BstIvtBoxstack::getY, y) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用")) |
|
|
|
); |
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(emptyPointsInColumn)) continue; |
|
|
|
|
|
|
|
List<String> emptyStackCodes = emptyPointsInColumn.stream() |
|
|
|
.map(BstIvtBoxstack::getStack_code) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
BstIvtBoxstack waitPoint = boxstackService.getOne( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.eq(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("等待区")) |
|
|
|
.eq(BstIvtBoxstack::getY, y) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用")) |
|
|
|
.notIn(BstIvtBoxstack::getX, x) |
|
|
|
.notIn(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("缓存区"))); |
|
|
|
currentCanUseCount += list.size(); |
|
|
|
} |
|
|
|
if (currentCanUseCount <= 3) { |
|
|
|
List<String> taskTypes = new ArrayList<>(Collections.singletonList(BoxStackEnum.TASK_TYPE.code("木箱入库"))); |
|
|
|
List<SchBaseTask> existTask = taskService.getExistTasks(taskTypes); |
|
|
|
if (existTask.size() >= 3) { |
|
|
|
throw new BadRequestException("木箱入库任务超过最大数量"); |
|
|
|
} |
|
|
|
} |
|
|
|
); |
|
|
|
|
|
|
|
if (waitPoint == null) continue; |
|
|
|
|
|
|
|
int waitingTasks = taskService.checkHaveTaskByNext(Arrays.asList(waitPoint.getStack_code())).size(); |
|
|
|
int assignTasks = taskService.checkHaveTaskByNext(emptyStackCodes).size(); |
|
|
|
|
|
|
|
if (emptyPointsInColumn.size() > waitingTasks + assignTasks) { |
|
|
|
JSONObject taskParam = buildTaskParam(firstMaterial, vehicleCodeStr, |
|
|
|
BoxStackEnum.AGV_ACTION_TYPE.code("放货二次分配"), |
|
|
|
BoxStackEnum.TASK_TYPE.code("木箱入库"), |
|
|
|
pointCode, waitPoint.getStack_code()); |
|
|
|
mxInTask.createTask(taskParam); |
|
|
|
return; |
|
|
|
} |
|
|
|
JSONObject taskParam = new JSONObject(); |
|
|
|
taskParam.put("material_code", box_specs.get(0)); |
|
|
|
taskParam.put("vehicle_code", vehicle_code); |
|
|
|
taskParam.put("vehicle_code2", BoxStackEnum.AGV_ACTION_TYPE.code("放货二次分配")); |
|
|
|
taskParam.put("task_type", BoxStackEnum.TASK_TYPE.code("木箱入库")); |
|
|
|
taskParam.put("point_code1", point_code); |
|
|
|
taskParam.put("point_code2", BoxStackEnum.AGV_WAIT_POINT.code("木箱等待点1")); |
|
|
|
mxInTask.createTask(reqParam); |
|
|
|
} |
|
|
|
} finally { |
|
|
|
if (lock.isLocked() && lock.isHeldByCurrentThread()) { |
|
|
|
lock.unlock(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
List<BstIvtBoxstack> firstRowEmpty = boxstackService.list( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.isNull(BstIvtBoxstack::getBox_spec) |
|
|
|
.eq(BstIvtBoxstack::getCurrent_layer_count, 0) |
|
|
|
.eq(BstIvtBoxstack::getX, 1) |
|
|
|
.eq(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("密集区")) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用")) |
|
|
|
); |
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(firstRowEmpty)) { |
|
|
|
throw new BadRequestException("无可用货位,请稍后入库"); |
|
|
|
} |
|
|
|
|
|
|
|
BstIvtBoxstack selectedStack = firstRowEmpty.get(0); |
|
|
|
BstIvtBoxstack waitPoint = boxstackService.getOne( |
|
|
|
new LambdaQueryWrapper<BstIvtBoxstack>() |
|
|
|
.eq(BstIvtBoxstack::getPoint_status, BoxStackEnum.POINT_STATUS.code("等待区")) |
|
|
|
.eq(BstIvtBoxstack::getY, selectedStack.getY()) |
|
|
|
.eq(BstIvtBoxstack::getIs_used, BoxStackEnum.IS_USED.code("启用")) |
|
|
|
); |
|
|
|
|
|
|
|
if (waitPoint == null) { |
|
|
|
throw new BadRequestException("未找到对应等待区位置,请检查配置"); |
|
|
|
} |
|
|
|
|
|
|
|
JSONObject taskParam = buildTaskParam(firstMaterial, vehicleCodeStr, |
|
|
|
BoxStackEnum.AGV_ACTION_TYPE.code("放货二次分配"), |
|
|
|
BoxStackEnum.TASK_TYPE.code("木箱入库"), |
|
|
|
pointCode, waitPoint.getStack_code()); |
|
|
|
mxInTask.createTask(taskParam); |
|
|
|
} |
|
|
|
|
|
|
|
private JSONObject buildTaskParam(String materialCode, String vehicleCode, |
|
|
|
String vehicleCode2, String taskType, |
|
|
|
String pointCode1, String pointCode2) { |
|
|
|
JSONObject obj = new JSONObject(); |
|
|
|
obj.put("material_code", materialCode); |
|
|
|
obj.put("vehicle_code", vehicleCode); |
|
|
|
obj.put("vehicle_code2", vehicleCode2); |
|
|
|
obj.put("task_type", taskType); |
|
|
|
obj.put("point_code1", pointCode1); |
|
|
|
obj.put("point_code2", pointCode2); |
|
|
|
return obj; |
|
|
|
} |
|
|
|
|
|
|
|
} |