liweidong
liweidong-hj 1 month ago
parent 5ceb90c34b
commit 88295c75f2
  1. 103
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/logistics/pojo/entity/VirtualShelves.java
  2. 100
      blade-service-api/blade-desk-api/src/main/java/org/springblade/desk/logistics/pojo/vo/AgvSchedulingTaskVO.java
  3. 11
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/mapper/VirtualShelvesMapper.java
  4. 12
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/mapper/VirtualShelvesMapper.xml
  5. 23
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/ITaskExecuteRecordService.java
  6. 49
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/IVirtualShelvesService.java
  7. 35
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/PipelineServiceImpl.java
  8. 127
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/TaskExecuteRecordServiceImpl.java
  9. 63
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/service/impl/VirtualShelvesServiceImpl.java
  10. 111
      blade-service/blade-desk/src/main/java/org/springblade/desk/logistics/utils/AgvTaskTypeUtil.java

@ -0,0 +1,103 @@
package org.springblade.desk.logistics.pojo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springblade.core.mp.base.BaseEntity;
import java.io.Serial;
/**
* 虚拟货架实体类
*
* @author
* @since
*/
@Data
@TableName("LM_VIRTUAL_SHELVES")
@Schema(description = "虚拟货架对象")
@EqualsAndHashCode(callSuper = true)
public class VirtualShelves extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 绑定状态常量已绑定
*/
public static final Integer BOUND_YES = 1;
/**
* 绑定状态常量未绑定
*/
public static final Integer BOUND_NO = 0;
/**
* 虚拟货架号
*/
@Schema(description = "虚拟货架号")
private String virtualShelvesCode;
/**
* 是否绑定 1:绑定 0:未绑定
*/
@Schema(description = "是否绑定 1:绑定 0:未绑定")
private Integer isBound;
/**
* 状态
*/
@Schema(description = "状态")
private Integer status;
/**
* 备注
*/
@Schema(description = "备注")
private String remark;
/**
* 获取绑定状态描述
* @return 绑定状态描述
*/
@Schema(description = "绑定状态描述")
public String getBoundStatusDesc() {
if (BOUND_YES.equals(this.isBound)) {
return "已绑定";
} else if (BOUND_NO.equals(this.isBound)) {
return "未绑定";
} else {
return "未知状态";
}
}
/**
* 判断是否已绑定
* @return true:已绑定 false:未绑定
*/
public boolean isBound() {
return BOUND_YES.equals(this.isBound);
}
/**
* 判断是否未绑定
* @return true:未绑定 false:已绑定
*/
public boolean isUnbound() {
return BOUND_NO.equals(this.isBound);
}
/**
* 绑定虚拟货架
*/
public void bind() {
this.isBound = BOUND_YES;
}
/**
* 解绑虚拟货架
*/
public void unbind() {
this.isBound = BOUND_NO;
}
}

@ -0,0 +1,100 @@
package org.springblade.desk.logistics.pojo.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* AGV调度任务返回VO
*
* @author
* @since
*/
@Data
@Schema(description = "AGV调度任务返回对象")
public class AgvSchedulingTaskVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 返回码0成功其他失败
*/
@Schema(description = "返回码(0:成功,其他:失败)")
private String code;
/**
* 任务单号
*/
@Schema(description = "任务单号")
private String taskCode;
/**
* 任务模板类型
* QM3: 输送线取货-电梯跨楼层
* QM5: 三楼站点取货放到三楼电梯接驳位到一层
* QM6: 同楼层放货放到输送线上
* QM7: 输送线取货放到普通梳齿架站点
*/
@Schema(description = "任务模板类型(QM3、QM5、QM6、QM7)")
private String taskTyp;
/**
* 开始位置
*/
@Schema(description = "开始位置")
private String startPos;
/**
* 结束位置
*/
@Schema(description = "结束位置")
private String endPos;
/**
* 位置类型00AGV 05CTU
*/
@Schema(description = "位置类型(00:AGV 05:CTU)")
private String type;
/**
* 返回消息
*/
@Schema(description = "返回消息")
private String message;
/**
* 是否成功
* @return true:成功 false:失败
*/
public boolean isSuccess() {
return "0".equals(this.code);
}
/**
* 构建成功对象
*/
public static AgvSchedulingTaskVO success(String taskCode, String taskTyp,
String startPos, String endPos, String type) {
AgvSchedulingTaskVO vo = new AgvSchedulingTaskVO();
vo.setCode("0");
vo.setTaskCode(taskCode);
vo.setTaskTyp(taskTyp);
vo.setStartPos(startPos);
vo.setEndPos(endPos);
vo.setType(type);
vo.setMessage("success");
return vo;
}
/**
* 构建失败对象
*/
public static AgvSchedulingTaskVO fail(String code, String message) {
AgvSchedulingTaskVO vo = new AgvSchedulingTaskVO();
vo.setCode(code);
vo.setMessage(message);
return vo;
}
}

@ -0,0 +1,11 @@
package org.springblade.desk.logistics.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springblade.desk.logistics.pojo.entity.VirtualShelves;
import java.util.List;
public interface VirtualShelvesMapper extends BaseMapper<VirtualShelves> {
List<VirtualShelves> getVirtualShelvesCode();
}

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "org.springblade.desk.logistics.mapper.VirtualShelvesMapper">
<select id="getVirtualShelvesCode" resultType="org.springblade.desk.logistics.pojo.entity.VirtualShelves">
select id, virtual_shelves_code, is_bound, status, create_user, create_time, create_dept, update_user, update_time, is_deleted, remark from LM_VIRTUAL_SHELVES
where IS_DELETED = 0 and IS_BOUND = 0
</select>
</mapper>

@ -30,6 +30,7 @@ import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.pojo.entity.AGVCallBack;
import org.springblade.desk.logistics.pojo.entity.OrderBind;
import org.springblade.desk.logistics.pojo.entity.TaskExecuteRecord;
import org.springblade.desk.logistics.pojo.vo.AgvSchedulingTaskVO;
/**
* 物流小车对接实体类 服务类
@ -40,4 +41,26 @@ import org.springblade.desk.logistics.pojo.entity.TaskExecuteRecord;
public interface ITaskExecuteRecordService extends BaseService<TaskExecuteRecord> {
R agvCallback(AGVCallBack agvCallBack);
/**
* 虚拟货架与起点站点信息进行绑定接口
* @param virtualShelvesCode 虚拟货架号
* @param stationCode 站点编码
* @return
*/
boolean boundVirtualShelves(String virtualShelvesCode,String stationCode);
/**
* 调用AGV小车 CTU
* @param taskTyp 任务类型 (QM3,QM5,QM6,QM7)
* @param startPos 开始位置
* @param endPos 结束位置
* @param type 位置类型00AGV 05CTU
* @return
*/
AgvSchedulingTaskVO genAgvSchedulingTask(String taskTyp, String startPos, String endPos, String type);
}

@ -0,0 +1,49 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.desk.logistics.service;
import org.springblade.core.mp.base.BaseService;
import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.pojo.entity.AGVCallBack;
import org.springblade.desk.logistics.pojo.entity.TaskExecuteRecord;
import org.springblade.desk.logistics.pojo.entity.VirtualShelves;
import java.util.List;
/**
* 虚拟货架 服务类
*
* @author BladeX
* @since 2025-11-12
*/
public interface IVirtualShelvesService extends BaseService<VirtualShelves> {
/**
* 获取未绑定的虚拟货架
* @return
*/
VirtualShelves getVirtualShelvesCode();
}

@ -26,11 +26,9 @@
package org.springblade.desk.logistics.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springblade.desk.logistics.pojo.entity.Location;
import org.springblade.desk.logistics.pojo.entity.Station;
import org.springblade.desk.logistics.pojo.entity.Task;
import org.springblade.desk.logistics.pojo.entity .WeighData;
import org.springblade.desk.logistics.pojo.entity.*;
import org.springblade.desk.logistics.service.*;
import org.springblade.desk.logistics.utils.AgvTaskTypeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springblade.core.log.exception.ServiceException;
@ -62,6 +60,15 @@ public class PipelineServiceImpl implements IPipelineService {
@Autowired
IOrderBoxService orderBoxService;
@Autowired
ITaskExecuteRecordService taskExecuteRecordService;
@Autowired
AgvTaskTypeUtil agvTaskTypeUtil;
@Autowired
IVirtualShelvesService virtualShelvesService;
@Override
public boolean verifyConveyorBoxWeighing(String boxBarcode, BigDecimal actualWeight, BigDecimal ratedWeight, Integer returnStatus) {
@ -138,7 +145,25 @@ public class PipelineServiceImpl implements IPipelineService {
}
log.info("站点有预占,将物料箱送往指定站点:{}", station.getStationCode());
// TODO 查询站点位置并调用AGV小车送往站点
String stationPosition = station.getStationPosition();
String taskType = agvTaskTypeUtil.getTaskType(Integer.parseInt(stationPosition));
// 获取虚拟货架
VirtualShelves virtualShelves = virtualShelvesService.getVirtualShelvesCode();
if(null == virtualShelves){
return false;
}
// 站点绑定虚拟货架
boolean shelves = taskExecuteRecordService.boundVirtualShelves(virtualShelves.getVirtualShelvesCode(),"1002");
if(!shelves){
return false;
}
// 调用AGV
taskExecuteRecordService.genAgvSchedulingTask(taskType,"1002",station.getStationCode(),"00");
return true;
}

@ -25,6 +25,10 @@
*/
package org.springblade.desk.logistics.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.mp.base.BaseServiceImpl;
@ -34,11 +38,16 @@ import org.springblade.desk.logistics.mapper.TaskExecuteRecordMapper;
import org.springblade.desk.logistics.pojo.entity.AGVCallBack;
import org.springblade.desk.logistics.pojo.entity.OrderBind;
import org.springblade.desk.logistics.pojo.entity.TaskExecuteRecord;
import org.springblade.desk.logistics.pojo.vo.AgvSchedulingTaskVO;
import org.springblade.desk.logistics.service.IOrderBindService;
import org.springblade.desk.logistics.service.ITaskExecuteRecordService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 物流小车对接实体类 服务实现类
@ -69,4 +78,122 @@ public class TaskExecuteRecordServiceImpl extends BaseServiceImpl<TaskExecuteRec
}
return R.success();
}
@Override
public boolean boundVirtualShelves(String virtualShelvesCode, String stationCode) {
String url = "http://172.22.252.10:8181/rcms/services/rest/hikRpcService/bindPodAndBerth";
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("reqCode", "TEXT" + IdUtil.fastSimpleUUID());
requestBody.put("podCode", virtualShelvesCode);
requestBody.put("berthCode", stationCode);
requestBody.put("podDir", "0");
requestBody.put("indBind", "1");
try {
String responseStr = HttpUtil.post(url, JSON.toJSONString(requestBody));
log.info("绑定虚拟货架响应:{}", responseStr);
JSONObject response = JSON.parseObject(responseStr);
String code = response.getString("code");
if ("0".equals(code)) {
log.info("绑定虚拟货架成功,虚拟货架:{},站点:{}", virtualShelvesCode, stationCode);
return true;
} else {
log.error("绑定虚拟货架失败,虚拟货架:{},站点:{},响应:{}",
virtualShelvesCode, stationCode, responseStr);
return false;
}
} catch (Exception e) {
log.error("绑定虚拟货架异常,虚拟货架:{},站点:{}", virtualShelvesCode, stationCode, e);
return false;
}
}
@Override
public AgvSchedulingTaskVO genAgvSchedulingTask(String taskTyp, String startPos, String endPos, String type) {
// 接口地址
String url = "http://172.22.252.10:8181/rcms/services/rest/hikRpcService/genAgvSchedulingTask";
// 生成唯一标识(请求标识和任务标识使用相同值,便于追踪)
String reqCode = generateReqCode("AGVT");
String taskCode = reqCode;
try {
// 构建请求参数
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("reqCode", reqCode);
requestBody.put("taskCode", taskCode);
requestBody.put("taskTyp", taskTyp);
// 构建位置路径列表
List<Map<String, String>> positionCodePath = new ArrayList<>();
// 起始位置
Map<String, String> startPosition = new HashMap<>();
startPosition.put("positionCode", startPos);
startPosition.put("type", type);
positionCodePath.add(startPosition);
// 目标位置
Map<String, String> endPosition = new HashMap<>();
endPosition.put("positionCode", endPos);
endPosition.put("type", type);
positionCodePath.add(endPosition);
requestBody.put("positionCodePath", positionCodePath);
// 记录请求日志
log.info("调用AGV调度任务接口,请求参数:{}", JSON.toJSONString(requestBody));
// 发送POST请求
String responseStr = HttpUtil.post(url, JSON.toJSONString(requestBody));
// 记录响应日志
log.info("调用AGV调度任务接口,响应结果:{}", responseStr);
if (!StringUtils.hasText(responseStr)) {
log.error("调用AGV调度任务接口失败,响应为空");
return new AgvSchedulingTaskVO();
}
// 解析响应
JSONObject response = JSON.parseObject(responseStr);
String code = response.getString("code");
String message = response.getString("message");
// 判断是否调用成功(code为0表示成功)
if ("0".equals(code)) {
log.info("AGV调度任务创建成功,任务类型:{},任务单号:{},起始位置:{},目标位置:{}",
taskTyp, taskCode, startPos, endPos);
return AgvSchedulingTaskVO.success( taskCode, taskTyp, startPos, endPos, type);
} else {
// code不等于0,输出错误信息
log.error("AGV调度任务创建失败,错误码:{},错误信息:{},任务类型:{},起始位置:{},目标位置:{}",
code, message, taskTyp, startPos, endPos);
return AgvSchedulingTaskVO.fail(code,message);
}
} catch (Exception e) {
log.error("调用AGV调度任务接口异常,任务类型:{},起始位置:{},目标位置:{}",
taskTyp, startPos, endPos, e);
return new AgvSchedulingTaskVO();
}
}
/**
* 生成请求唯一标识
* @param prefix 前缀
* @return 唯一标识
*/
private String generateReqCode(String prefix) {
// 使用Hutool的IdUtil生成唯一ID
return prefix + IdUtil.fastSimpleUUID();
}
}

@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.desk.logistics.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.desk.logistics.mapper.VirtualShelvesMapper;
import org.springblade.desk.logistics.pojo.entity.VirtualShelves;
import org.springblade.desk.logistics.service.IVirtualShelvesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* 虚拟货架 服务实现类
*
* @author BladeX
* @since 2025-11-12
*/
@Service
@Slf4j
public class VirtualShelvesServiceImpl extends BaseServiceImpl<VirtualShelvesMapper, VirtualShelves> implements IVirtualShelvesService {
@Autowired
VirtualShelvesMapper virtualShelvesMapper;
@Override
public VirtualShelves getVirtualShelvesCode() {
List<VirtualShelves> virtualShelvesList = virtualShelvesMapper.getVirtualShelvesCode();
if(CollectionUtils.isEmpty(virtualShelvesList)){
log.error("暂无虚拟货架表");
return null;
}
VirtualShelves virtualShelves = virtualShelvesList.get(0);
log.info("虚拟货架号:{}", virtualShelves.getVirtualShelvesCode());
return virtualShelves;
}
}

@ -0,0 +1,111 @@
package org.springblade.desk.logistics.utils;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
* AGV任务类型工具类
*/
@Slf4j
@Service
public class AgvTaskTypeUtil {
/**
* 获取AGV任务类型
* @param floor 楼层 (1:一层, 3:三层)
* @param isRecycle 是否回收 (true:空箱回收, false:正常任务)
* @return 任务类型 (QM3, QM5, QM6, QM7)
*/
public String getTaskType(Integer floor, Boolean isRecycle) {
// 参数校验
if (floor == null) {
log.error("楼层参数不能为空");
throw new IllegalArgumentException("楼层参数不能为空");
}
// 默认isRecycle为false
isRecycle = isRecycle != null ? isRecycle : false;
String taskType;
switch (floor) {
case 1:
// 一层
if (isRecycle) {
// 一层空箱回收 -> QM6 (同楼层放货放到输送线上)
taskType = "QM6";
log.info("一层空箱回收任务,使用QM6");
} else {
// 一层正常任务 -> QM7 (输送线取货放到普通梳齿架站点)
taskType = "QM7";
log.info("一层正常任务,使用QM7");
}
break;
case 3:
// 三层
if (isRecycle) {
// 三层空箱回收 -> QM5 (三楼站点取货放到三楼电梯接驳位到一层)
taskType = "QM5";
log.info("三层空箱回收任务,使用QM5");
} else {
// 三层正常任务 -> QM3 (输送线取货-电梯跨楼层)
taskType = "QM3";
log.info("三层正常任务,使用QM3");
}
break;
default:
log.error("不支持的楼层:{}", floor);
throw new IllegalArgumentException("不支持的楼层:" + floor);
}
return taskType;
}
/**
* 获取AGV任务类型简化版只传楼层
* @param floor 楼层
* @return 任务类型
*/
public String getTaskType(Integer floor) {
return getTaskType(floor, false);
}
/**
* 判断是否为跨楼层任务
* @param taskType 任务类型
* @return true:跨楼层 false:同楼层
*/
public boolean isCrossFloor(String taskType) {
return "QM3".equals(taskType) || "QM5".equals(taskType);
}
/**
* 判断是否为回收任务
* @param taskType 任务类型
* @return true:回收任务 false:正常任务
*/
public boolean isRecycleTask(String taskType) {
return "QM5".equals(taskType) || "QM6".equals(taskType);
}
/**
* 获取任务描述
* @param taskType 任务类型
* @return 任务描述
*/
public String getTaskDescription(String taskType) {
switch (taskType) {
case "QM3":
return "输送线取货-电梯跨楼层(小车不做电梯),货物放电梯接驳位,三楼小车接货后放到目标站点";
case "QM5":
return "三楼站点取货放到三楼电梯接驳位到一层后,一楼小车取电梯内货物放到输送线(空箱回收)";
case "QM6":
return "同楼层放货放到输送线上(不跨楼层)";
case "QM7":
return "输送线取货放到普通梳齿架站点(不跨电梯仅同楼层)";
default:
return "未知任务类型";
}
}
}
Loading…
Cancel
Save