rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/WcsServiceImpl.java
@@ -1,12 +1,25 @@
package com.vincent.rsf.server.api.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.CoercionAction;
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
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.config.RemotesInfoProperties;
import com.vincent.rsf.server.api.entity.CommonResponse;
import com.vincent.rsf.server.api.entity.constant.RcsConstant;
import com.vincent.rsf.server.api.entity.dto.InTaskMsgDto;
import com.vincent.rsf.server.api.entity.dto.LocTypeDto;
import com.vincent.rsf.server.api.controller.params.TaskInParam;
import com.vincent.rsf.server.api.controller.erp.params.TaskInParam;
import com.vincent.rsf.server.api.entity.dto.SyncLocsDto;
import com.vincent.rsf.server.api.entity.params.CommonRequest;
import com.vincent.rsf.server.api.entity.params.WcsTaskParams;
import com.vincent.rsf.server.manager.enums.*;
import com.vincent.rsf.server.api.service.WcsService;
import com.vincent.rsf.server.api.utils.LocUtils;
@@ -17,16 +30,26 @@
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.manager.enums.LocStsType;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
public class WcsServiceImpl implements WcsService {
    @Autowired
@@ -51,6 +74,10 @@
    private WaitPakinItemService waitPakinItemService;
    @Autowired
    private BasStationService basStationService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private RemotesInfoProperties.RcsApi rcsApi;
    @Override
@@ -84,8 +111,6 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public InTaskMsgDto createInTask(TaskInParam param) {
        // 获取库位号
        InTaskMsgDto locNo = getLocNo(param);
        // 验证设备站点
        DeviceSite deviceSite = validateDeviceSite(param);
@@ -95,6 +120,14 @@
        // 生成任务编码
        String ruleCode = generateTaskCode();
        // 获取库位号
        InTaskMsgDto locNo = null;
        try {
            locNo = getLocNo(param);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 创建并保存任务
        Task task = createTask(ruleCode, locNo.getLocNo(), waitPakin.getBarcode(),
@@ -138,7 +171,7 @@
    private WaitPakin validateWaitPakin(String barcode) {
        WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>()
                .eq(WaitPakin::getBarcode, barcode)
                .eq(WaitPakin::getIoStatus, Short.parseShort(PakinIOStatus.PAKIN_IO_STATUS_DONE.val)));
                .eq(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val));
        if (Cools.isEmpty(waitPakin)) {
            throw new CoolException("请检查组拖状态是否完成!!");
@@ -166,7 +199,7 @@
        task.setTaskCode(ruleCode)
                .setTaskStatus(TaskStsType.GENERATE_IN.id)
                .setTaskType(TaskType.TASK_TYPE_IN.type)
                .setWarehType(WarehType.WAREHOUSE_TYPE_CRN.id)
                .setWarehType(WarehType.WAREHOUSE_TYPE_CRN.val)
                .setTargLoc(targetLoc)
                .setBarcode(barcode)
                .setTargSite(targetSite)
@@ -188,7 +221,6 @@
                .eq(Loc::getCode, locCode)
                .set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_S.type)
                .set(Loc::getBarcode, barcode));
        if (!updated) {
            throw new CoolException("库位预约失败!!");
        }
@@ -198,9 +230,7 @@
     * 获取并验证组拖明细
     */
    private List<WaitPakinItem> getWaitPakinItems(Long pakinId) {
        List<WaitPakinItem> waitPakinItems = waitPakinItemService.list(
                new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, pakinId));
        List<WaitPakinItem> waitPakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, pakinId));
        if (waitPakinItems.isEmpty()) {
            throw new CoolException("数据错误:组拖明细不存在");
        }
@@ -328,7 +358,7 @@
//        return locNo;
//    }
    @Override
    public InTaskMsgDto getLocNo(TaskInParam param) {
    public InTaskMsgDto getLocNo(TaskInParam param) throws Exception {
        String matnr = null;
        String batch = null;
        List<WaitPakin> waitPakins = waitPakinService.list(new LambdaQueryWrapper<WaitPakin>().eq(WaitPakin::getBarcode, param.getBarcode()));
@@ -355,23 +385,97 @@
        }
        LocTypeDto locTypeDto = new LocTypeDto(param);
        InTaskMsgDto dto = null;
        switch (warehouseArea.getType()) {
            case "CRN": //堆垛机
                dto = getLocNoCrn(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
                break;
            case "SXC": //四向库
                break;
            case "CTU": //ctu
                dto = getLocNoCtu(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
                break;
            default:
                dto = getLocNoCtu(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
                break;
        //根据立库类型获取获取库位
        if (warehouseArea.getType().equals(WarehType.WAREHOUSE_TYPE_CRN.val)) {
            //堆垛机
            dto = getLocNoCrn(param.getOrgLoc(), deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
        } else if (warehouseArea.getType().equals(WarehType.WAREHOUSE_TYPE_FOUR_DIRECTIONS.val)) {
            //四向库
        } else {
            //CTU 库以及其它
            dto = getLocNoCtu(deviceBind, warehouseArea.getId(), param.getSourceStaNo(), matnr, batch, locTypeDto, 0, param.getIoType());
        }
        return dto;
    }
    private InTaskMsgDto getLocNoCrn(DeviceBind deviceBind, Long area, Integer sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
    /**
     * @author Ryan
     * @date 2025/8/28
     * @description: Wcs库位同步
     * @version 1.0
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R syncLocsToWms() {
        /**RCS基础配置链接*/
        CommonRequest params = new CommonRequest();
        params.setPageSize(100);
        ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
        scheduled.scheduleWithFixedDelay(new Runnable() {
            int current = 1;
            @Override
            public void run() {
                params.setCurrent(current);
                List<SyncLocsDto> dtos = syncLocs(params);
                if (dtos.isEmpty()) {
                    scheduled.shutdown();
                }
                dtos.forEach(dto -> {
                    Loc loc = new Loc();
                    loc.setCode(dto.getLocNo())
                            .setBarcode(dto.getBarcode())
                            .setRow(dto.getRow())
                            .setLev(dto.getLev())
                            .setId(null)
                            .setAreaId(41L)
                            .setWarehouseId(27L)
                            .setCol(dto.getBay())
                            .setType(dto.getLocType())
                            .setStatus(dto.getStatusBool())
                            .setUseStatus(LocStsType.getLocSts(dto.getLocSts()));
                    if (!locService.save(loc)) {
                        scheduled.shutdown();
                        throw new CoolException("WCS库位同步失败!!");
                    }
                });
                current++;
            }
        }, 1, 1, TimeUnit.SECONDS);
        return R.ok();
    }
    private List<SyncLocsDto> syncLocs(CommonRequest params) {
        String rcsUrl = rcsApi.getHost() + ":" + rcsApi.getPort() + RcsConstant.syncLocs;
        log.info("任务下发,请求地址: {}, 请求参数: {}", rcsUrl, JSONObject.toJSONString(params));
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        headers.add("api-version", "v2.0");
        HttpEntity httpEntity = new HttpEntity(params, headers);
        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
        log.info("任务下发后,响应结果: {}", exchange);
        if (Objects.isNull(exchange.getBody())) {
            throw new CoolException("任务下发失败!!");
        } else {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.coercionConfigDefaults()
                    .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
            try {
                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
                if (result.getCode() == 200) {
                    JSONObject jsonObject = JSONObject.parseObject(exchange.getBody()).getJSONObject("data");
                    return JSONArray.parseArray(jsonObject.getJSONArray("records").toJSONString(), SyncLocsDto.class);
                } else {
                    throw new CoolException("任务下发失败!!");
                }
            } catch (JsonProcessingException e) {
                throw new CoolException(e.getMessage());
            }
        }
    }
    private InTaskMsgDto getLocNoCrn(String orgLoc, DeviceBind deviceBind, Long area, Integer sourceStaNo, String matnr, String batch, LocTypeDto locTypeDto, int times, Integer ioType) {
        if (Cools.isEmpty(matnr)) {  //物料号
            matnr = "";
        }
@@ -379,7 +483,7 @@
            batch = "";
        }
        // 初始化参数
        int deviceNo = 0;      //堆垛机号
        int channel = 0;      //堆垛机号
        int nearRow = 0;    //最浅库位排
        int curRow = 0;     //最深库位排
        int rowCount = 0;   //轮询轮次
@@ -387,44 +491,40 @@
        InTaskMsgDto inTaskMsgDto = new InTaskMsgDto();
        int sRow = deviceBind.getStartRow();
        int eRow = deviceBind.getEndRow();
        int deviceQty = deviceBind.getDeviceQty();
        // ===============>>>> 开始执行
        curRow = deviceBind.getCurrentRow();
        //此程序用于优化堆垛机异常时的运行时间
        for (int i = times; i <= deviceQty * 2; i++) {
            int[] locNecessaryParameters = LocUtils.LocNecessaryParameters(deviceBind, curRow, deviceQty);
            curRow = locNecessaryParameters[1];
            deviceNo = locNecessaryParameters[2];
            rowCount = locNecessaryParameters[0];
            nearRow = locNecessaryParameters[3];
            break;
        }
        //获取排
        int[] locNecessaryParameters = LocUtils.LocNecessaryParameters(deviceBind, curRow, deviceQty);
        curRow = locNecessaryParameters[1];
        channel = locNecessaryParameters[2];
        rowCount = locNecessaryParameters[0];
        nearRow = locNecessaryParameters[3];
        if (nearRow == 0) {
            throw new CoolException("无可用堆垛机");
        }
        //入库靠近摆放
        if (ioType == 1 && deviceBind.getBeSimilar().equals("1") && !Cools.isEmpty(matnr)) {
        if (ioType == 1 && deviceBind.getBeSimilar().equals("1") && !Cools.isEmpty(matnr) && Cools.isEmpty(orgLoc)) {
            if (nearRow != curRow) {
                List<LocItem> locItems = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getMatnrCode, matnr));
                for (LocItem locItem : locItems) {
                    Loc loc1 = locService.getById(locItem.getLocId());
                    if (LocUtils.isShallowLoc(slaveProperties, loc1.getCode())) {
                    if (LocUtils.isShallowLoc(loc1.getCode())) {
                        continue;
                    }
                    String shallowLocNo = LocUtils.getShallowLoc(slaveProperties, loc1.getCode());
                    String shallowLocNo = LocUtils.getShallowLoc(loc1.getCode());
                    // 检测目标库位是否为空库位
                    Loc shallowLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, shallowLocNo));
                    if (shallowLoc != null && shallowLoc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) {
                        if (LocUtils.locMoveCheckLocTypeComplete(shallowLoc, locTypeDto)) {
                            loc = shallowLoc;
                            deviceNo = shallowLoc.getDeviceNo();
                            channel = shallowLoc.getChannel();
                            break;
                        }
                    }
                }
@@ -459,10 +559,10 @@
        DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                .eq(DeviceSite::getType, ioType)
                .eq(DeviceSite::getSite, sourceStaNo)
                .eq(DeviceSite::getDeviceCode, deviceNo)
                .eq(DeviceSite::getChannel, channel)
        );
        if (Cools.isEmpty(deviceSite)) {
            deviceNo = 0;
            channel = 0;
        } else {
            inTaskMsgDto.setStaNo(Integer.parseInt(deviceSite.getDeviceSite()));
        }
@@ -474,7 +574,7 @@
        // 开始查找库位 ==============================>>
        // 1.按规则查找库位
        if (Cools.isEmpty(loc) && deviceNo != 0) {
        if (Cools.isEmpty(loc) && channel != 0) {
            List<Loc> locMasts = null;
            locMasts = locService.list(new LambdaQueryWrapper<Loc>()
                    .eq(Loc::getRow, nearRow)
@@ -484,11 +584,15 @@
                    .orderByAsc(Loc::getLev)
                    .orderByAsc(Loc::getCol)
            );
            Loc orgMoveLoc = null;
            if (!Cools.isEmpty(orgLoc)) {
                orgMoveLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, orgLoc));
            }
            for (Loc locMast1 : locMasts) {
                if (!LocUtils.locMoveCheckLocTypeComplete(locMast1, locTypeDto)) {
                    continue;
                }
                String shallowLoc = LocUtils.getDeepLoc(slaveProperties, locMast1.getCode());
                String shallowLoc = LocUtils.getDeepLoc(locMast1.getCode());
                if ((ioType == 1 && deviceBind.getBeSimilar().equals("1"))) {
                    //相似物料打开,判断深库位有没有货,没货就放深库位,有货就不操作
                    Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
@@ -496,7 +600,13 @@
                            .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                            .eq(Loc::getAreaId, area)
                    );
                    if (!Cools.isEmpty(locMast2)) {
                        if (null != orgMoveLoc) {
                            if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                break;
                            }
                        }
                        loc = locMast2;
                        break;
                    }
@@ -507,7 +617,13 @@
                            .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type, LocStsType.LOC_STS_TYPE_F.type)
                            .eq(Loc::getAreaId, area)
                    );
                    if (!Cools.isEmpty(locMast2)) {
                        if (null != orgMoveLoc) {
                            if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                break;
                            }
                        }
                        loc = locMast1;
                        break;
                    } else {
@@ -516,7 +632,13 @@
                                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                                .eq(Loc::getAreaId, area)
                        );
                        if (!Cools.isEmpty(locMast2)) {
                            if (null != orgMoveLoc) {
                                if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                    break;
                                }
                            }
                            loc = locMast2;
                            break;
                        }
@@ -529,13 +651,19 @@
                        continue;
                    }
                    if (deviceBind.getBeSimilar().equals("1")) {
                        String shallowLoc = LocUtils.getDeepLoc(slaveProperties, locMast1.getCode());
                        String shallowLoc = LocUtils.getDeepLoc(locMast1.getCode());
                        Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>()
                                .eq(Loc::getCode, shallowLoc)
                                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                                .eq(Loc::getAreaId, area)
                        );
                        if (!Cools.isEmpty(locMast2)) {
                            if (null != orgMoveLoc) {
                                if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                    break;
                                }
                            }
                            loc = locMast2;
                            break;
                        } else {
@@ -544,7 +672,13 @@
                                    .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type, LocStsType.LOC_STS_TYPE_D.type)
                                    .eq(Loc::getAreaId, area)
                            );
                            if (!Cools.isEmpty(locMast2)) {
                                if (null != orgMoveLoc) {
                                    if (!locMast2.getChannel().equals(orgMoveLoc.getChannel())) {
                                        break;
                                    }
                                }
                                loc = locMast1;
                                break;
                            }
@@ -561,7 +695,7 @@
        //查询当前库位类型空库位 小于5个则locmast = null
        List<Loc> locTypeLocMasts = locService.list(new LambdaQueryWrapper<Loc>()
                .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type)
                .eq(Loc::getChannel, deviceNo)
                .eq(Loc::getChannel, channel)
                .eq(Loc::getType, locTypeDto.getLocType1())
                .eq(Loc::getAreaId, area)
        );
@@ -573,21 +707,21 @@
            // 当前巷道无空库位时,递归调整至下一巷道,检索全部巷道无果后,跳出递归
            if (times < rowCount * 2) {
                times = times + 1;
                return getLocNoCrn(deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, times, ioType);
                return getLocNoCrn(orgLoc, deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, times, ioType);
            }
            // 2.库位当前所属尺寸无空库位时,调整尺寸参数,向上兼容检索库位
            if (locTypeDto.getLocType1() < 3) {
                int i = locTypeDto.getLocType1() + 1;
                locTypeDto.setLocType1(i);
                return getLocNoCrn(deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, 0, ioType);
                return getLocNoCrn(orgLoc, deviceBind, area, sourceStaNo, matnr, batch, locTypeDto, 0, ioType);
            }
            throw new CoolException("没有空库位");
        }
        String locNo = loc.getCode();
        // 返回dto
        inTaskMsgDto.setDeviceNo(deviceNo);
        inTaskMsgDto.setDeviceNo(channel);
        inTaskMsgDto.setSourceStaNo(sourceStaNo);
//        inTaskMsgDto.setStaNo();
        inTaskMsgDto.setLocNo(locNo);