package com.vincent.rsf.server.api.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.common.Cools; import com.vincent.rsf.framework.common.R; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.api.controller.erp.params.SyncOrderParams; import com.vincent.rsf.server.api.controller.erp.params.SyncOrdersItem; import com.vincent.rsf.server.api.service.AgvService; import com.vincent.rsf.server.api.service.ReceiveMsgService; import com.vincent.rsf.server.manager.controller.params.WaitPakinParam; import com.vincent.rsf.server.manager.entity.*; import com.vincent.rsf.server.manager.entity.param.AgvBindAndInTParam; import com.vincent.rsf.server.manager.enums.LocStsType; import com.vincent.rsf.server.manager.enums.PakinIOStatus; import com.vincent.rsf.server.manager.enums.StationTypeEnum; import com.vincent.rsf.server.manager.enums.TaskType; import com.vincent.rsf.server.manager.service.*; import com.vincent.rsf.server.manager.service.impl.DeviceSiteServiceImpl; import com.vincent.rsf.server.manager.service.impl.MatnrServiceImpl; import com.vincent.rsf.server.manager.utils.LocManageUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @Service public class AgvServiceImpl implements AgvService { private static final String AGV_MISC_ORDER_PREFIX = "AGVT"; private static final DateTimeFormatter BATCH_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); @Autowired private ReceiveMsgService receiveMsgService; @Autowired private WaitPakinService waitPakinService; @Autowired private BasStationService basStationService; @Autowired private WaitPakinItemService waitPakinItemService; @Autowired private WarehouseAreasService warehouseAreasService; @Autowired private LocService locService; @Autowired private TaskService taskService; @Autowired private BasContainerService basContainerService; @Autowired private DeviceSiteServiceImpl deviceSiteService; @Autowired private MatnrServiceImpl matnrService; @Override @Transactional(rollbackFor = Exception.class) public R AGVBindAndInTaskStart(WaitPakinParam waitPakinPda, Long loginUserId) { //先绑定 getAGVStaBind(waitPakinPda); //生成任务 AGVInTaskStart(waitPakinPda, loginUserId); return R.ok(); } @Override @Transactional(rollbackFor = Exception.class) public R AGVBindAndInTaskStart(String barcode) { //验证条码 boolean b = checkStaStatus(barcode); if (!b) { return R.error("条码未找到对应规则"); } return R.ok(); } @Override @Transactional(rollbackFor = Exception.class) public boolean AGVBindAndInTaskStart(String barcode, String sta) { //验证条码 checkStaStatus(barcode,sta); return true; } @Override @Transactional(rollbackFor = Exception.class) public R AGVBindAndInTaskStartT(AgvBindAndInTParam param, Long loginUserId) { if (Objects.isNull(param)) { throw new CoolException("参数不能为空!!"); } if (Cools.isEmpty(param.getMatNr())) { throw new CoolException("物料编码不能为空!!"); } if (Cools.isEmpty(param.getPalletBarcode())) { throw new CoolException("托盘码不能为空!!"); } if (Cools.isEmpty(param.getPalletSta())) { throw new CoolException("操作站点不能为空!!"); } checkPalletBarcodeAvailable(param.getPalletBarcode()); Matnr matnr = matnrService.getOne(new LambdaQueryWrapper() .eq(Matnr::getCode, param.getMatNr()) .last("limit 1")); if (Objects.isNull(matnr)) { throw new CoolException("物料编码不存在:" + param.getMatNr()); } long nowMillis = System.currentTimeMillis(); long nowSeconds = nowMillis / 1000; String orderNo = buildAgvMiscOrderNo(param.getPalletBarcode(), nowMillis); SyncOrderParams syncOrder = new SyncOrderParams() .setWkType(SyncOrderParams.BusinessType.STK_MISCELLANEOUS) .setType("in") .setOrderNo(orderNo) .setOrderInternalCode(nowMillis) .setCreateTime(nowSeconds) .setBusinessTime(nowSeconds) .setAnfme(1.0) .setOrderItems(Collections.singletonList(buildOrderItem(param, matnr, nowMillis, nowSeconds).setBarcode(param.getPalletBarcode()))); syncOrder.setStationId(param.getPalletSta()); R r = receiveMsgService.syncCheckOrder(Collections.singletonList(syncOrder), loginUserId); if (!Objects.isNull(r) && Objects.equals(String.valueOf(r.get("code")), "200")) { autoCallAgvInTask(param, loginUserId); } return R.ok(Cools.add("orderNo", orderNo).add("palletBarcode", param.getPalletBarcode())); } private void autoCallAgvInTask(AgvBindAndInTParam param, Long loginUserId) { CompletableFuture.runAsync(() -> { int retry = 0; while (retry < 5) { retry++; try { Thread.sleep(1000); WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, param.getPalletBarcode()) .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val) .last("limit 1")); if (Objects.isNull(waitPakin)) { continue; } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, param.getPalletSta()) .last("limit 1")); if (Objects.isNull(basStation)) { return; } // if (Objects.isNull(basStation) || Objects.isNull(basStation.getArea())) { // return; // } WaitPakinParam waitPakinPda = new WaitPakinParam() .setBarcode(param.getPalletBarcode()) .setStaNo(param.getPalletSta()) .setArea(param.getArea()); AGVInTaskStart(waitPakinPda, loginUserId); return; } catch (Exception ignored) { } } }); } private SyncOrdersItem buildOrderItem(AgvBindAndInTParam param, Matnr matnr, long uniqueSeed, long nowSeconds) { String suffix = String.valueOf(uniqueSeed); String batch = LocalDateTime.ofInstant(Instant.ofEpochSecond(nowSeconds), ZoneId.systemDefault()) .format(BATCH_FORMATTER); SyncOrdersItem syncOrdersItem = new SyncOrdersItem() .setModel(matnr.getModel()) .setAnfme(1.0) .setUnit(matnr.getUnit()) .setBaseUnitId(matnr.getUnit()) .setPriceUnitId(matnr.getUnit()) .setBatch(batch) .setPlanNo(AGV_MISC_ORDER_PREFIX + "-PLAN-" + suffix) .setLineId(AGV_MISC_ORDER_PREFIX + "-LINE-" + suffix) .setPlatItemId("1") .setPalletId(param.getPalletBarcode()); syncOrdersItem.setMatnr(matnr.getCode()); syncOrdersItem.setMaktx(matnr.getName()); syncOrdersItem.setSpec(matnr.getSpec()); return syncOrdersItem; } private String buildAgvMiscOrderNo(String palletBarcode, long uniqueSeed) { String cleanBarcode = palletBarcode.replaceAll("[^A-Za-z0-9]", ""); if (cleanBarcode.length() > 24) { cleanBarcode = cleanBarcode.substring(cleanBarcode.length() - 24); } return AGV_MISC_ORDER_PREFIX + cleanBarcode + uniqueSeed; } private void checkPalletBarcodeAvailable(String palletBarcode) { WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, palletBarcode) .last("limit 1")); if (!Objects.isNull(waitPakin)) { throw new CoolException("托盘码:" + palletBarcode + "已被组托单:" + waitPakin.getCode() + "使用!!"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getBarcode, palletBarcode) .last("limit 1")); if (!Objects.isNull(basStation)) { throw new CoolException("托盘码:" + palletBarcode + "已被站点:" + basStation.getStationName() + "绑定!!"); } Loc loc = locService.getOne(new LambdaQueryWrapper() .eq(Loc::getBarcode, palletBarcode) .last("limit 1")); if (!Objects.isNull(loc)) { throw new CoolException("托盘码:" + palletBarcode + "已被库位:" + loc.getCode() + "使用!!"); } } @Override public R getStaMsgSelect(Map params) { String sta = params.get("sta").toString(); if (Cools.isEmpty(sta)) { throw new CoolException("接驳位条码不能为空"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, sta) .eq(BasStation::getUseStatus, LocStsType.LOC_STS_TYPE_O.type) ); if (Cools.isEmpty(basStation)) { throw new CoolException("未找到接驳站点信息,请检查站点状态"); } List ids = basStation.getCrossZoneArea().stream() .map(Integer::longValue) .collect(Collectors.toList()); // ids.add(basStation.getArea()); // if (basStation.getIsCrossZone() == 1) { // String content = basStation.getCrossZoneArea().substring(1,.length() - 1); // String[] parts = content.split(","); // for (int i = 0; i < parts.length; i++) { // ids.add(Long.parseLong(parts[i].trim())); // } // } List warehouseAreasList = warehouseAreasService.list(new LambdaQueryWrapper() .in(WarehouseAreas::getId, ids) ); return R.ok(Cools .add("barcode", basStation.getBarcode()) .add("warehouseAreasList", warehouseAreasList) .add("area", basStation.getArea()) ); } @Override @Transactional(rollbackFor = Exception.class) public R AGVInTaskStart(WaitPakinParam waitPakinPda, Long loginUserId) { String sta = waitPakinPda.getStaNo(); String area = waitPakinPda.getArea(); if (Cools.isEmpty(sta)) { throw new CoolException("起点不能为空"); } if (Cools.isEmpty(area)) { throw new CoolException("目标库区不能为空"); } DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper() .eq(DeviceSite::getSite, sta) .eq(DeviceSite::getAreaIdEnd, Long.parseLong(area)) .eq(DeviceSite::getType, TaskType.TASK_TYPE_IN.type).last("limit 1")); if (Cools.isEmpty(deviceSite)) { throw new CoolException("无可用路径!!"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, sta) .eq(BasStation::getUseStatus, LocStsType.LOC_STS_TYPE_F.type) ); if (Cools.isEmpty(basStation)) { throw new CoolException("未找到起点站点信息,请检查站点状态"); } if (Cools.isEmpty(basStation.getBarcode())) { throw new CoolException("数据错误,接驳站无条码信息"); } WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, basStation.getBarcode()) .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val) ); if (Cools.isEmpty(waitPakin)) { throw new CoolException("未找到组托数据,请检查状态"); } String targetLoc = LocManageUtil.getTargetLoc(Long.parseLong(area)); taskService.generateAGVTasks(waitPakin, targetLoc, sta, deviceSite.getDeviceCode(),loginUserId); if (!basStation.getType().equals(0)){ basStation.setUseStatus(LocStsType.LOC_STS_TYPE_R.type); } if (!basStationService.updateById(basStation)) { throw new CoolException("更新站点状态失败"); } return R.ok(); } @Override public R AGVStaUnBind(Map params) { String sta = params.get("sta").toString(); if (Cools.isEmpty(sta)) { throw new CoolException("接驳位条码不能为空"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, sta) .eq(BasStation::getUseStatus, LocStsType.LOC_STS_TYPE_F.type) ); if (Cools.isEmpty(basStation)) { throw new CoolException("未找到接驳站点信息,请检查站点状态"); } if (Cools.isEmpty(basStation.getBarcode())) { throw new CoolException("数据错误,接驳站无条码信息"); } WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, basStation.getBarcode()) .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val) ); if (Cools.isEmpty(waitPakin)) { throw new CoolException("未找到组托数据,请检查状态"); } basStation.setBarcode(null); basStation.setUseStatus(LocStsType.LOC_STS_TYPE_O.type); if (!basStationService.updateById(basStation)) { throw new CoolException("更新站点状态失败"); } return R.ok("解绑成功"); } @Override public R getStaBindList(Map params) { String sta = params.get("sta").toString(); if (Cools.isEmpty(sta)) { throw new CoolException("接驳位条码不能为空"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, sta) .eq(BasStation::getUseStatus, LocStsType.LOC_STS_TYPE_F.type) ); if (Cools.isEmpty(basStation)) { throw new CoolException("未找到接驳站点信息,请检查站点状态"); } if (Cools.isEmpty(basStation.getBarcode())) { throw new CoolException("数据错误,接驳站无条码信息"); } WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, basStation.getBarcode()) .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val) ); if (Cools.isEmpty(waitPakin)) { throw new CoolException("未找到组托数据,请检查状态"); } List waitPakinItems = waitPakinItemService.list(new LambdaQueryWrapper().eq(WaitPakinItem::getPakinId, waitPakin.getId())); if (Cools.isEmpty(waitPakinItems)) { throw new CoolException("数据错误,未找到组托明细"); } List ids = basStation.getCrossZoneArea().stream() .map(Integer::longValue) .collect(Collectors.toList()); // ids.add(basStation.getArea()); // if (basStation.getIsCrossZone() == 1) { // String content = basStation.getCrossZoneArea().substring(1, basStation.getCrossZoneArea().length() - 1); // String[] parts = content.split(","); // for (int i = 0; i < parts.length; i++) { // ids.add(Long.parseLong(parts[i].trim())); // } // } List warehouseAreasList = warehouseAreasService.list(new LambdaQueryWrapper() .in(WarehouseAreas::getId, ids) ); return R.ok(Cools .add("barcode", basStation.getBarcode()) .add("list", waitPakinItems) .add("warehouseAreasList", warehouseAreasList) .add("area", basStation.getArea()) ); } @Override @Transactional(rollbackFor = Exception.class) public R getAGVStaBind(WaitPakinParam waitPakinPda) { String barcode = waitPakinPda.getBarcode(); String sta = waitPakinPda.getStaNo(); //验证基础信息 BasStation basStation = checkStaStatus(barcode, sta,waitPakinPda.getArea()); //更新站点状态 if (!basStation.getType().equals(0)){ basStation.setUseStatus(LocStsType.LOC_STS_TYPE_F.type); } basStation.setBarcode(barcode); if (!basStationService.updateById(basStation)) { throw new CoolException("更新站点状态失败"); } return R.ok("绑定成功"); } private BasStation checkStaStatus(String barcode, String sta) { if (Cools.isEmpty(barcode)) { throw new CoolException("容器码不能为空"); } if (Cools.isEmpty(sta)) { throw new CoolException("站点信息不能为空"); } BasStation isBarcodeSta = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getBarcode, barcode) .last("limit 1") ); if (!Cools.isEmpty(isBarcodeSta)) { throw new CoolException("该条码已被" + isBarcodeSta.getStationName() + "站绑定"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, sta) ); if (Cools.isEmpty(basStation)) { throw new CoolException("未找到站点信息"); } if (!Cools.isEmpty(basStation.getContainerType())) { List longs1 = basStation.getContainerType().stream() .map(Integer::longValue) .collect(Collectors.toList()); List containers = basContainerService.list( new LambdaQueryWrapper() .in(BasContainer::getContainerType, longs1) ); boolean matches = false; for (BasContainer container : containers) { String codeType = container.getCodeType(); // 获取正则表达式 if (barcode.matches(codeType)) { // 判断条码是否符合这个正则 matches = true; break; // 找到匹配的就退出循环 } } if (!matches) { throw new CoolException("条码与站点不匹配"); } } else { throw new CoolException("数据异常:验证基础信息"); } return basStation; } private BasStation checkStaStatus(String barcode, String sta,String area) { if (Cools.isEmpty(barcode)) { throw new CoolException("容器码不能为空"); } if (Cools.isEmpty(sta)) { throw new CoolException("接驳位条码不能为空"); } WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, barcode) .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val) ); if (Cools.isEmpty(waitPakin)) { throw new CoolException("容器码未找到组托信息,请检查组托状态"); } BasStation isBarcodeSta = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getBarcode, barcode) , false ); if (!Cools.isEmpty(isBarcodeSta)) { throw new CoolException("该条码已被" + isBarcodeSta.getStationName() + "站绑定"); } BasStation basStation = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getStationName, sta) ); if (Cools.isEmpty(basStation)) { throw new CoolException("未找到站点信息"); } if (!basStation.getUseStatus().equals("O")) { throw new CoolException("站点状态不为空闲"); } if (basStation.getType().equals(StationTypeEnum.STATION_TYPE_MUTI.type)) { throw new CoolException("站点为光电站点,禁止呼叫AGV"); } if (!basStation.getCrossZoneArea().contains(Integer.parseInt(area))) { throw new CoolException("当前站点不支持目标库区"); } if (!Cools.isEmpty(basStation.getContainerType())) { List longs1 = basStation.getContainerType().stream() .map(Integer::longValue) .collect(Collectors.toList()); List containers = basContainerService.list( new LambdaQueryWrapper() .in(BasContainer::getContainerType, longs1) ); boolean matches = false; boolean matches2 = true; for (BasContainer container : containers) { String codeType = container.getCodeType(); // 获取正则表达式 if (barcode.matches(codeType)) { // 判断条码是否符合这个正则 List areaList2 = container.getAreasIds(); if (!areaList2.contains(Integer.valueOf(area))) { matches2 = false; continue; } matches = true; break; // 找到匹配的就退出循环 } } // boolean matches = containers.stream() // .map(BasContainer::getCodeType) // .anyMatch(codeType -> barcode.matches(codeType)); if (!matches2) { throw new CoolException("查询到的容器不支持目标库区"); } if (!matches) { throw new CoolException("容器与站点不匹配"); } } else { throw new CoolException("数据异常:验证基础信息"); } return basStation; } private boolean checkStaStatus(String barcode) { if (Cools.isEmpty(barcode)) { throw new CoolException("容器码不能为空"); } WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper() .eq(WaitPakin::getBarcode, barcode) .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val) ); if (Cools.isEmpty(waitPakin)) { throw new CoolException("容器码未找到组托信息,请检查组托状态"); } BasStation isBarcodeSta = basStationService.getOne(new LambdaQueryWrapper() .eq(BasStation::getBarcode, barcode) , false ); if (!Cools.isEmpty(isBarcodeSta)) { throw new CoolException("该条码已被" + isBarcodeSta.getStationName() + "站绑定"); } List containers = basContainerService.list( new LambdaQueryWrapper()); boolean matches = false; for (BasContainer container : containers) { String codeType = container.getCodeType(); // 获取正则表达式 if (barcode.matches(codeType)) { // 判断条码是否符合这个正则 matches = true; return true; } } // boolean matches = containers.stream() // .map(BasContainer::getCodeType) // .anyMatch(codeType -> barcode.matches(codeType)); if (!matches) { return false; } return true; } }