1
11 小时以前 c68748f06719e770f5026e042e08b2ab751d065b
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/AgvServiceImpl.java
@@ -1,30 +1,50 @@
package com.vincent.rsf.server.api.service.impl;
import com.alibaba.fastjson.JSONObject;
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 lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@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;
@@ -35,17 +55,25 @@
    @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);
        try{
            getAGVStaBind(waitPakinPda);
        } catch (CoolException e) {
            log.error(e.getMessage());
        }
        //生成任务
        AGVInTaskStart(waitPakinPda, loginUserId);
        return R.ok();
@@ -63,6 +91,148 @@
    }
    @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<Matnr>()
                .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(3000);
                    WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>()
                            .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<BasStation>()
                            .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());
                    AGVBindAndInTaskStart(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<WaitPakin>()
                .eq(WaitPakin::getBarcode, palletBarcode)
                .last("limit 1"));
        if (!Objects.isNull(waitPakin)) {
            throw new CoolException("托盘码:" + palletBarcode + "已被组托单:" + waitPakin.getCode() + "使用!!");
        }
        BasStation basStation = basStationService.getOne(new LambdaQueryWrapper<BasStation>()
                .eq(BasStation::getBarcode, palletBarcode)
                .last("limit 1"));
        if (!Objects.isNull(basStation)) {
            throw new CoolException("托盘码:" + palletBarcode + "已被站点:" + basStation.getStationName() + "绑定!!");
        }
        Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getBarcode, palletBarcode)
                .last("limit 1"));
        if (!Objects.isNull(loc)) {
            throw new CoolException("托盘码:" + palletBarcode + "已被库位:" + loc.getCode() + "使用!!");
        }
    }
    @Override
    public R getStaMsgSelect(Map<String, Object> params) {
        String sta = params.get("sta").toString();
        if (Cools.isEmpty(sta)) {
@@ -75,15 +245,17 @@
        if (Cools.isEmpty(basStation)) {
            throw new CoolException("未找到接驳站点信息,请检查站点状态");
        }
        List<Long> ids = new ArrayList<>();
        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<Long> 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<WarehouseAreas> warehouseAreasList = warehouseAreasService.list(new LambdaQueryWrapper<WarehouseAreas>()
                .in(WarehouseAreas::getId, ids)
        );
@@ -102,17 +274,26 @@
        String sta = waitPakinPda.getStaNo();
        String area = waitPakinPda.getArea();
        if (Cools.isEmpty(sta)) {
            throw new CoolException("接驳位条码不能为空");
            throw new CoolException("起点不能为空");
        }
        if (Cools.isEmpty(area)) {
            throw new CoolException("目标库区不能为空");
        }
        DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                .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<BasStation>()
                .eq(BasStation::getStationName, sta)
                .eq(BasStation::getUseStatus, LocStsType.LOC_STS_TYPE_F.type)
        );
        if (Cools.isEmpty(basStation)) {
            throw new CoolException("未找到接驳站点信息,请检查站点状态");
            throw new CoolException("未找到起点站点信息,请检查站点状态");
        }
        if (Cools.isEmpty(basStation.getBarcode())) {
            throw new CoolException("数据错误,接驳站无条码信息");
@@ -126,17 +307,10 @@
        }
        String targetLoc = LocManageUtil.getTargetLoc(Long.parseLong(area));
        DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                .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("无可用路径!!");
        taskService.generateAGVTasks(waitPakin, targetLoc, sta, deviceSite.getDeviceCode(),loginUserId);
        if (!basStation.getType().equals(0)){
            basStation.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
        }
        taskService.generateAGVTasks(waitPakin, targetLoc, sta, deviceSite.getTarget(),loginUserId);
        basStation.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
        if (!basStationService.updateById(basStation)) {
            throw new CoolException("更新站点状态失败");
        }
@@ -204,15 +378,17 @@
        if (Cools.isEmpty(waitPakinItems)) {
            throw new CoolException("数据错误,未找到组托明细");
        }
        List<Long> ids = new ArrayList<>();
        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<Long> 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<WarehouseAreas> warehouseAreasList = warehouseAreasService.list(new LambdaQueryWrapper<WarehouseAreas>()
                .in(WarehouseAreas::getId, ids)
        );
@@ -234,7 +410,9 @@
        //验证基础信息
        BasStation basStation = checkStaStatus(barcode, sta,waitPakinPda.getArea());
        //更新站点状态
        basStation.setUseStatus(LocStsType.LOC_STS_TYPE_F.type);
        if (!basStation.getType().equals(0)){
            basStation.setUseStatus(LocStsType.LOC_STS_TYPE_F.type);
        }
        basStation.setBarcode(barcode);
        if (!basStationService.updateById(basStation)) {
            throw new CoolException("更新站点状态失败");
@@ -248,18 +426,12 @@
            throw new CoolException("容器码不能为空");
        }
        if (Cools.isEmpty(sta)) {
            throw new CoolException("接驳位条码不能为空");
            throw new CoolException("站点信息不能为空");
        }
        WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>()
                .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<BasStation>()
                        .eq(BasStation::getBarcode, barcode)
                , false
                .last("limit 1")
        );
        if (!Cools.isEmpty(isBarcodeSta)) {
            throw new CoolException("该条码已被" + isBarcodeSta.getStationName() + "站绑定");
@@ -270,11 +442,10 @@
        if (Cools.isEmpty(basStation)) {
            throw new CoolException("未找到站点信息");
        }
        if (!basStation.getUseStatus().equals("O")) {
            throw new CoolException("站点状态不为空闲");
        }
        if (!Cools.isEmpty(basStation.getContainerType())) {
            List<Long> longs1 = JSONObject.parseArray(basStation.getContainerType(), Long.class);
            List<Long> longs1 = basStation.getContainerType().stream()
                    .map(Integer::longValue)
                    .collect(Collectors.toList());
            List<BasContainer> containers = basContainerService.list(
                    new LambdaQueryWrapper<BasContainer>()
                            .in(BasContainer::getContainerType, longs1)
@@ -287,9 +458,6 @@
                    break;  // 找到匹配的就退出循环
                }
            }
//            boolean matches = containers.stream()
//                    .map(BasContainer::getCodeType)
//                    .anyMatch(codeType -> barcode.matches(codeType));
            if (!matches) {
                throw new CoolException("条码与站点不匹配");
            }
@@ -333,12 +501,13 @@
            throw new CoolException("站点为光电站点,禁止呼叫AGV");
        }
        List<String> areaList = JSONObject.parseArray(basStation.getCrossZoneArea(), String.class);
        if (!areaList.contains(area)) {
        if (!basStation.getCrossZoneArea().contains(Integer.parseInt(area))) {
            throw new CoolException("当前站点不支持目标库区");
        }
        if (!Cools.isEmpty(basStation.getContainerType())) {
            List<Long> longs1 = JSONObject.parseArray(basStation.getContainerType(), Long.class);
            List<Long> longs1 = basStation.getContainerType().stream()
                    .map(Integer::longValue)
                    .collect(Collectors.toList());
            List<BasContainer> containers = basContainerService.list(
                    new LambdaQueryWrapper<BasContainer>()
                            .in(BasContainer::getContainerType, longs1)
@@ -348,7 +517,7 @@
            for (BasContainer container : containers) {
                String codeType = container.getCodeType();  // 获取正则表达式
                if (barcode.matches(codeType)) {  // 判断条码是否符合这个正则
                    List<Integer> areaList2 = container.getAreas();
                    List<Integer> areaList2 = container.getAreasIds();
                    if (!areaList2.contains(Integer.valueOf(area))) {
                        matches2 = false;
                        continue;