自动化立体仓库 - WMS系统
zwl
13 小时以前 519f42d527a1dd4917455609855f732b1eb0f7a0
新增接口:
给mes提供机台及对应机台工位的冻结情况
4个文件已添加
3个文件已修改
491 ■■■■■ 已修改文件
src/main/java/com/zy/api/controller/HmesApiController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/controller/params/HmesBackLocParams.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/entity/dto/HmesDeviceFreezeStatusDto.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/entity/dto/HmesStationFreezeStatusDto.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/service/HmesApiService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/service/impl/HmesApiServiceImpl.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
version/doc/HMES对接接口说明_20260320.md 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/api/controller/HmesApiController.java
@@ -5,6 +5,7 @@
import com.core.annotations.ManagerAuth;
import com.core.common.Cools;
import com.core.common.R;
import com.zy.api.controller.params.HmesBackLocParams;
import com.zy.api.controller.params.ReceviceTaskParams;
import com.zy.api.service.HmesApiService;
import com.zy.asrs.entity.param.OpenOrderPakoutParam;
@@ -16,6 +17,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Objects;
@@ -76,5 +78,41 @@
        return hmesApiService.releaseLock(params);
    }
    /**
     * èŽ·å–æœºå°åŠå¯¹åº”å·¥ä½å†»ç»“æƒ…å†µ
     * @author Ryan
     * @date 2026/3/20 11:06
     * @return com.core.common.R
     */
//    @ManagerAuth
    @ApiOperation("获取机台及对应工位冻结情况")
    @GetMapping("/work/freeze/status")
    public R getDeviceFreezeStatus() {
        return hmesApiService.getDeviceFreezeStatus();
    }
    /**
     * ç©ºæ¡¶/余料回库
     * @author Ryan
     * @date 2026/3/20 11:28
     * @param params
     * @return com.core.common.R
     */
//    @ManagerAuth
    @ApiOperation("空桶/余料回库")
    @PostMapping("/work/back/loc")
    public R backLoc(@RequestBody HmesBackLocParams params) {
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        if (Cools.isEmpty(params.getLocNo()) && Cools.isEmpty(params.getWorkNo()) && Cools.isEmpty(params.getBarcode())) {
            return R.error("库位号[locNo]、工作号[workNo]、条码[barcode]不能同时为空");
        }
        if (Cools.isEmpty(params.getBackType())) {
            return R.error("回库类型[backType]不能为空");
        }
        return hmesApiService.backLoc(params);
    }
}
src/main/java/com/zy/api/controller/params/HmesBackLocParams.java
New file
@@ -0,0 +1,26 @@
package com.zy.api.controller.params;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@ApiModel(value = "HmesBackLocParams", description = "HMES空桶/余料回库参数")
public class HmesBackLocParams implements Serializable {
    @ApiModelProperty("库位号,MES优先使用该字段定位任务档目标库位")
    private String locNo;
    @ApiModelProperty("工作号,优先使用该字段定位回库任务")
    private String workNo;
    @ApiModelProperty("条码,locNo、workNo为空时使用该字段定位任务")
    private String barcode;
    @ApiModelProperty("回库类型:EMPTY/4/空桶/空板 æˆ– SURPLUS/5/余料")
    private String backType;
}
src/main/java/com/zy/api/entity/dto/HmesDeviceFreezeStatusDto.java
New file
@@ -0,0 +1,27 @@
package com.zy.api.entity.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
@Data
@Accessors(chain = true)
@ApiModel(value = "HmesDeviceFreezeStatusDto", description = "HMES机台冻结状态")
public class HmesDeviceFreezeStatusDto implements Serializable {
    @ApiModelProperty("机台号")
    private String devNo;
    @ApiModelProperty("机台类型编码")
    private String devType;
    @ApiModelProperty("冻结标记,Y:冻结 N:未冻结")
    private String freeze;
    @ApiModelProperty("工位冻结明细")
    private List<HmesStationFreezeStatusDto> stationList;
}
src/main/java/com/zy/api/entity/dto/HmesStationFreezeStatusDto.java
New file
@@ -0,0 +1,26 @@
package com.zy.api.entity.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@Accessors(chain = true)
@ApiModel(value = "HmesStationFreezeStatusDto", description = "HMES机台工位冻结状态")
public class HmesStationFreezeStatusDto implements Serializable {
    @ApiModelProperty("工位库位号")
    private String locNo;
    @ApiModelProperty("机台工位顺序")
    private Integer orderNo;
    @ApiModelProperty("工位类型")
    private String locType;
    @ApiModelProperty("冻结标记,Y:冻结 N:未冻结")
    private String freeze;
}
src/main/java/com/zy/api/service/HmesApiService.java
@@ -1,6 +1,7 @@
package com.zy.api.service;
import com.core.common.R;
import com.zy.api.controller.params.HmesBackLocParams;
import com.zy.api.controller.params.ReceviceTaskParams;
import com.zy.asrs.entity.param.OpenOrderPakoutParam;
@@ -9,4 +10,8 @@
    R pubWorkTask(OpenOrderPakoutParam params);
    R releaseLock(ReceviceTaskParams params);
    R getDeviceFreezeStatus();
    R backLoc(HmesBackLocParams params);
}
src/main/java/com/zy/api/service/impl/HmesApiServiceImpl.java
@@ -6,6 +6,9 @@
import com.core.common.R;
import com.core.common.SnowflakeIdWorker;
import com.core.exception.CoolException;
import com.zy.api.controller.params.HmesBackLocParams;
import com.zy.api.entity.dto.HmesDeviceFreezeStatusDto;
import com.zy.api.entity.dto.HmesStationFreezeStatusDto;
import com.zy.api.controller.params.ReceviceTaskParams;
import com.zy.api.service.HmesApiService;
import com.zy.api.service.WcsApiService;
@@ -28,6 +31,8 @@
@Service
@Slf4j
public class HmesApiServiceImpl implements HmesApiService {
    private static final Long HMES_USER_ID = 9995L;
    @Autowired
    private BasDeviceService basDeviceService;
@@ -61,6 +66,12 @@
    private OrderDetlPakoutServiceImpl orderDetlPakoutService;
    @Autowired
    private LocDetlServiceImpl locDetlService;
    @Autowired
    private WrkMastService wrkMastService;
    @Autowired
    private WorkService workService;
    /**
@@ -238,4 +249,124 @@
        });
        return R.ok(s+"成功 ï¼ï¼");
    }
    @Override
    public R getDeviceFreezeStatus() {
        List<BasDevice> devices = basDeviceService.selectList(new EntityWrapper<BasDevice>()
                .eq("status", 1)
                .orderBy("dev_no", true));
        if (Cools.isEmpty(devices)) {
            return R.ok(Collections.emptyList());
        }
        List<LocAroundBind> binds = locAroundBindService.selectList(new EntityWrapper<LocAroundBind>()
                .orderBy("dev_no", true)
                .orderBy("order_no", true)
                .orderBy("id", true));
        Map<String, List<LocAroundBind>> bindMap = new HashMap<>();
        if (!Cools.isEmpty(binds)) {
            bindMap = binds.stream()
                    .filter(bind -> !Cools.isEmpty(bind.getDevNo()))
                    .collect(Collectors.groupingBy(LocAroundBind::getDevNo, LinkedHashMap::new, Collectors.toList()));
        }
        List<HmesDeviceFreezeStatusDto> result = new ArrayList<>();
        for (BasDevice device : devices) {
            List<LocAroundBind> deviceBinds = bindMap.getOrDefault(device.getType(), Collections.emptyList());
            List<HmesStationFreezeStatusDto> stationList = deviceBinds.stream()
                    .sorted(Comparator.comparing(LocAroundBind::getOrderNo, Comparator.nullsLast(Integer::compareTo))
                            .thenComparing(LocAroundBind::getId, Comparator.nullsLast(Long::compareTo)))
                    .map(this::buildStationFreezeStatus)
                    .collect(Collectors.toList());
            boolean frozen = stationList.stream().anyMatch(station -> "Y".equals(station.getFreeze()));
            result.add(new HmesDeviceFreezeStatusDto()
                    .setDevNo(device.getDevNo())
                    .setDevType(device.getType())
                    .setFreeze(toFreezeFlag(frozen ? 1 : 0))
                    .setStationList(stationList));
        }
        return R.ok(result);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R backLoc(HmesBackLocParams params) {
        Integer backType = parseBackType(params.getBackType());
        if (backType == null) {
            return R.error("回库类型[backType]仅支持 EMPTY/4/空桶/空板 æˆ– SURPLUS/5/余料");
        }
        WrkMast mast = findBackLocWrkMast(params);
        if (Objects.isNull(mast)) {
            return R.error("未查询到可回库的任务档");
        }
        if (!Objects.equals(mast.getWrkSts(), 15L)) {
            return R.error("当前任务状态不能执行回库!");
        }
        workService.updateWrkIsSuplus(String.valueOf(mast.getWrkNo()), HMES_USER_ID, backType);
        return workService.backLocOperation(String.valueOf(mast.getWrkNo()), HMES_USER_ID);
    }
    private HmesStationFreezeStatusDto buildStationFreezeStatus(LocAroundBind bind) {
        return new HmesStationFreezeStatusDto()
                .setLocNo(bind.getBlocNo())
                .setOrderNo(bind.getOrderNo())
                .setLocType(bind.getLocType())
                .setFreeze(toFreezeFlag(bind.getFreeze()));
    }
    private String toFreezeFlag(Integer freeze) {
        return Objects.equals(freeze, 1) ? "Y" : "N";
    }
    private Integer parseBackType(String backType) {
        if (Cools.isEmpty(backType)) {
            return null;
        }
        String normalized = backType.trim().toUpperCase(Locale.ROOT);
        if ("4".equals(normalized) || "EMPTY".equals(normalized) || "EMPTY_BUCKET".equals(normalized)
                || "EMPTY_PLATE".equals(normalized) || "空桶".equals(backType.trim())
                || "空板".equals(backType.trim())) {
            return 4;
        }
        if ("5".equals(normalized) || "SURPLUS".equals(normalized) || "SUPLUS".equals(normalized)
                || "LEFTOVER".equals(normalized) || "余料".equals(backType.trim())) {
            return 5;
        }
        return null;
    }
    private WrkMast findBackLocWrkMast(HmesBackLocParams params) {
        if (!Cools.isEmpty(params.getLocNo())) {
            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                    .eq("loc_no", params.getLocNo())
                    .orderBy("appe_time", false)
                    .orderBy("wrk_no", false));
            WrkMast matched = pickLatestCompletedWrkMast(wrkMasts);
            if (matched != null) {
                return matched;
            }
        }
        if (!Cools.isEmpty(params.getWorkNo())) {
            return wrkMastService.selectById(params.getWorkNo());
        }
        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                .eq("barcode", params.getBarcode())
                .orderBy("appe_time", false)
                .orderBy("wrk_no", false));
        return pickLatestCompletedWrkMast(wrkMasts);
    }
    private WrkMast pickLatestCompletedWrkMast(List<WrkMast> wrkMasts) {
        if (Cools.isEmpty(wrkMasts)) {
            return null;
        }
        for (WrkMast wrkMast : wrkMasts) {
            if (Objects.equals(wrkMast.getWrkSts(), 15L)) {
                return wrkMast;
            }
        }
        return wrkMasts.get(0);
    }
}
version/doc/HMES¶Ô½Ó½Ó¿Ú˵Ã÷_20260320.md
New file
@@ -0,0 +1,238 @@
# HMES å¯¹æŽ¥æŽ¥å£è¯´æ˜Ž
## 1. æ–‡æ¡£è¯´æ˜Ž
- æ–‡æ¡£ä¸»é¢˜ï¼šHMES ä¸Ž WCS æ–°å¢žæŽ¥å£å¯¹æŽ¥è¯´æ˜Ž
- é€‚用范围:机台冻结状态查询、空桶/余料回库
- æ›´æ–°æ—¶é—´ï¼š2026-03-20
- æœåŠ¡å‰ç¼€ï¼š`/api`
## 2. é€šç”¨çº¦å®š
### 2.1 è¯·æ±‚协议
- åè®®ï¼šHTTP
- æ•°æ®æ ¼å¼ï¼š`application/json`
- å­—符集:`UTF-8`
### 2.2 å“åº”格式
系统统一使用 `R` ç»“构返回,基础格式如下:
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {}
}
```
字段说明:
- `code`:业务状态码,`200` è¡¨ç¤ºæˆåŠŸï¼Œ`500` è¡¨ç¤ºå¤±è´¥
- `msg`:业务提示信息
- `data`:返回数据体,仅对象/数组类成功结果返回该字段
说明:
- å½“接口返回 `R.ok(对象/数组)` æ—¶ï¼Œä¸šåŠ¡æ•°æ®æ”¾åœ¨ `data`
- å½“接口返回 `R.ok("成功消息")` æ—¶ï¼Œä»…返回 `code` + `msg`,不返回 `data`
## 3. æŽ¥å£ä¸€ï¼šèŽ·å–æœºå°åŠå¯¹åº”å·¥ä½å†»ç»“æƒ…å†µ
### 3.1 æŽ¥å£ç›®æ ‡
供 MES æŸ¥è¯¢å½“前所有启用机台,以及每台机台绑定工位的冻结状态。
### 3.2 è¯·æ±‚信息
- è¯·æ±‚路径:`GET /api/work/freeze/status`
- è¯·æ±‚方式:`GET`
- è¯·æ±‚参数:无
### 3.3 è¿”回规则
- æŸ¥è¯¢èŒƒå›´ï¼š`asr_bas_device.status = 1` çš„启用机台
- å·¥ä½æ•°æ®æ¥æºï¼š`asr_loc_around_bind`
- æœºå°å†»ç»“状态判定:
  - åªè¦è¯¥æœºå°ä»»ä¸€ç»‘定工位 `freeze = 1`,则机台 `freeze = "Y"`
  - å¦åˆ™æœºå° `freeze = "N"`
- å·¥ä½å†»ç»“状态判定:
  - `freeze = 1` è¿”回 `"Y"`
  - å…¶ä»–返回 `"N"`
### 3.4 æˆåŠŸè¿”å›žç¤ºä¾‹
```json
{
  "code": 200,
  "msg": "操作成功",
  "data": [
    {
      "devNo": "M01",
      "devType": "JT01",
      "freeze": "Y",
      "stationList": [
        {
          "locNo": "01010101",
          "orderNo": 1,
          "locType": "R",
          "freeze": "Y"
        },
        {
          "locNo": "01010102",
          "orderNo": 2,
          "locType": "R",
          "freeze": "N"
        }
      ]
    },
    {
      "devNo": "M02",
      "devType": "JT02",
      "freeze": "N",
      "stationList": []
    }
  ]
}
```
### 3.5 è¿”回字段说明
机台字段:
- `devNo`:机台号
- `devType`:机台类型编码
- `freeze`:机台冻结标记,`Y` å†»ç»“,`N` æœªå†»ç»“
- `stationList`:该机台绑定工位列表
工位字段:
- `locNo`:工位对应库位号
- `orderNo`:机台工位顺序
- `locType`:工位状态类型
- `freeze`:工位冻结标记,`Y` å†»ç»“,`N` æœªå†»ç»“
### 3.6 å¤±è´¥åœºæ™¯
该接口正常情况下返回成功;若服务异常,则返回:
```json
{
  "code": 500,
  "msg": "服务器错误"
}
```
## 4. æŽ¥å£äºŒï¼šç©ºæ¡¶/余料回库
### 4.1 æŽ¥å£ç›®æ ‡
供 MES åœ¨æœºå°ä½œä¸šå®ŒæˆåŽï¼Œå‘起空桶回库或余料回库。
### 4.2 è¯·æ±‚信息
- è¯·æ±‚路径:`POST /api/work/back/loc`
- è¯·æ±‚方式:`POST`
- è¯·æ±‚类型:`application/json`
### 4.3 è¯·æ±‚参数
```json
{
  "locNo": "01010101",
  "backType": "EMPTY"
}
```
字段说明:
- `locNo`:库位号,MES æŽ¨èä½¿ç”¨è¯¥å­—段,系统按任务档目标库位 `loc_no` åŒ¹é…
- `workNo`:工作号,兼容保留
- `barcode`:条码,兼容保留
- `backType`:回库类型,必填
`backType` æ”¯æŒå€¼ï¼š
- `EMPTY`
- `4`
- `空桶`
- `空板`
- `SURPLUS`
- `5`
- `余料`
类型映射规则:
- `EMPTY / 4 / ç©ºæ¡¶ / ç©ºæ¿`:按空桶回库处理,系统内部映射为 `isSuplus = 0`
- `SURPLUS / 5 / ä½™æ–™`:按余料回库处理,系统内部映射为 `isSuplus = 1`
### 4.4 å¤„理规则
1. è‹¥ä¼ å…¥ `locNo`,系统优先按任务档目标库位 `loc_no = locNo` æŸ¥è¯¢
2. è‹¥ `locNo` æœªä¼ ï¼Œå†æŒ‰ `workNo` æŸ¥è¯¢
3. è‹¥ `locNo`、`workNo` å‡ä¸ºç©ºï¼Œåˆ™æŒ‰ `barcode` æŸ¥è¯¢
4. å½“同一条件命中多条任务档时,优先取 `wrk_sts = 15` çš„æœ€æ–°ä»»åŠ¡
5. ä»»åŠ¡çŠ¶æ€å¿…é¡»ä¸º `15`,否则不允许执行回库
6. ç³»ç»Ÿå…ˆæ›´æ–°ä»»åŠ¡æ¡£å›žåº“ç±»åž‹ï¼Œå†è°ƒç”¨çŽ°æœ‰å›žåº“ä»»åŠ¡ç”Ÿæˆæµç¨‹
### 4.5 æˆåŠŸè¿”å›žç¤ºä¾‹
```json
{
  "code": 200,
  "msg": "生成回库任务成功!!"
}
```
### 4.6 å¤±è´¥è¿”回示例
1. å‚数缺失
```json
{
  "code": 500,
  "msg": "库位号[locNo]、工作号[workNo]、条码[barcode]不能同时为空"
}
```
2. å›žåº“类型错误
```json
{
  "code": 500,
  "msg": "回库类型[backType]仅支持 EMPTY/4/空桶/空板 æˆ– SURPLUS/5/余料"
}
```
3. æœªæ‰¾åˆ°ä»»åŠ¡
```json
{
  "code": 500,
  "msg": "未查询到可回库的任务档"
}
```
4. ä»»åŠ¡çŠ¶æ€ä¸å…è®¸å›žåº“
```json
{
  "code": 500,
  "msg": "当前任务状态不能执行回库!"
}
```
## 5. è”调注意事项
- å»ºè®® MES å‘起回库时优先传 `workNo`,避免同一条码存在多条历史任务时定位歧义
- å½“前推荐 MES ç›´æŽ¥ä¼  `locNo + backType`
- è‹¥åŒä¸€åº“位命中多条历史任务,系统会优先选取状态为 `15` çš„æœ€æ–°ä»»åŠ¡
- è‹¥ MES åªä¼ æ¡ç ï¼Œç³»ç»Ÿä¹Ÿä¼šä¼˜å…ˆé€‰å–状态为 `15` çš„æœ€æ–°ä»»åŠ¡
- å›žåº“接口只负责生成回库任务,不代表堆垛机已完成入库
- æœºå°å†»ç»“查询接口返回的是当前系统实时状态,MES ä¸åº”自行缓存过久
## 6. æœ¬æ¬¡æ–°å¢žæŽ¥å£æ¸…单
- `GET /api/work/freeze/status`
- `POST /api/work/back/loc`