Commit 28937bae zshaohui

1.AGV接口对接功能开发

1 个父辈 174e0d21
......@@ -85,5 +85,20 @@ public enum OP_STATUS {
/**
* 异常
*/
ABNORMAL;
ABNORMAL,
/**
* 任务状态
* EXECUTING=已发送到AGV
* IN_ON_LINE=入库时人员将料箱推上线体,
* IN_ON_AGV=入库时AGV从线体上抓起料箱
* OUT_ON_LINE=出库时AGV将料箱放上线体,
* OUT_ON_AGV=出库时AGV将料箱从库位中取出
* FINISHED=任务完成(入库时AGV将料箱放入库位;出库时料箱到达工位)
*/
IN_ON_LINE,
IN_ON_AGV,
OUT_ON_LINE,
OUT_ON_AGV,
ERROR;
}
......@@ -579,7 +579,7 @@ public class StoragePosController {
//如果料仓不可用,不能出库
if (!storage.isVirtual()) {
if (!dataCache.StorageIsAvailable(storage)) {
throw new ValidateException("smfcore.storage.notAvailable", "料仓{0}离线或不可用,无法出库", new String[]{storage.getName()});
//throw new ValidateException("smfcore.storage.notAvailable", "料仓{0}离线或不可用,无法出库", new String[]{storage.getName()});
}
}
......
......@@ -263,6 +263,8 @@ public class DataLog extends BasePo implements Serializable ,Comparable<DataLog>
private String shelfLoc;
private String currentLoc;
public String getBarcode() {
if(barcode == null){
return "";
......
package com.neotel.smfcore.custom.aiqingzhiyin1643.bean;
import lombok.Data;
@Data
public class CtuCheckOutTask {
private String barcode;
private String posName;
private String status;
private String checkOutLoc;
}
package com.neotel.smfcore.custom.aiqingzhiyin1643.bean;
import lombok.Data;
@Data
public class CtuPutInTask {
private String barcode;
private String posName;
private String status;
private String putInLoc;
}
package com.neotel.smfcore.custom.aiqingzhiyin1643.bean;
import lombok.Data;
@Data
public class Station {
private String name;
//上一个箱子
private String lastBox;
//当前箱子
private String currentBox;
}
package com.neotel.smfcore.custom.aiqingzhiyin1643.controller;
import com.google.common.collect.Lists;
import com.neotel.smfcore.common.bean.ReelLockPosInfo;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.exception.ValidateException;
import com.neotel.smfcore.common.utils.ReelLockPosUtil;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.barcode.utils.CodeResolve;
import com.neotel.smfcore.core.device.enums.OP_STATUS;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.storage.enums.DeviceType;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.custom.aiqingzhiyin1643.bean.CtuCheckOutTask;
import com.neotel.smfcore.custom.aiqingzhiyin1643.bean.CtuPutInTask;
import com.neotel.smfcore.custom.aiqingzhiyin1643.bean.Station;
import com.neotel.smfcore.custom.aiqingzhiyin1643.util.BoxUtil;
import com.neotel.smfcore.custom.aiqingzhiyin1643.util.StationCacheUtil;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
@Api(tags = "agv对接")
@Slf4j
@RestController
public class AgvDeviceController {
@Autowired
private TaskService taskService;
@Autowired
private CodeResolve codeResolve;
@Autowired
private DataCache dataCache;
@ApiOperation("获取库位号(1.如果有入库任务,直接返回库位号 2.如果没有入库任务,则生成一个入库任务,返回库位号)")
@RequestMapping("/service/store/agvBox/generatePutInTask")
@AnonymousAccess
public ResultBean generatePutInTask(HttpServletRequest request) {
String boxStr = request.getParameter("barcode");
String currentLoc = request.getParameter("currentLoc");
log.info("收到获取库位号的接口,料箱为:" + boxStr + ",当前位置为:" + currentLoc);
Map<String, String> resultMap = new HashMap<>();
resultMap.put("boxStr", "");
resultMap.put("posName", "");
try {
Barcode barcode = codeResolve.resolveOneValideBarcode("=2x2=" + boxStr);
if (StringUtils.isNotEmpty(barcode.getPosName())) {
return ResultBean.newErrorResult(-1, "smfcore.materialBox.inPos", "物料已在库位{0}中", new String[]{barcode.getBarcode()});
}
//判断有没有任务
DataLog dataLog = null;
List<DataLog> allTasks = taskService.getAllTasks();
for (DataLog task : allTasks) {
if (task.isPutInTask() && !task.isCancel() && !task.isFinished()) {
if (barcode.getBarcode().equals(task.getBarcode())) {
dataLog = task;
break;
}
}
}
if (dataLog != null) {
resultMap.put("boxStr", barcode.getBarcode());
resultMap.put("posName", dataLog.getPosName());
} else {
//如果任务不存在,则创建新任务
List<Storage> storageList = new ArrayList<>();
List<String> cidList = Lists.newArrayList();
for (Storage storage : dataCache.getAllStorage().values()) {
storageList.add(storage);
cidList.add(storage.getCid());
}
StoragePos pos = taskService.findEmptyPosForPutIn(storageList, barcode, "", "");
if (pos == null) {
return ResultBean.newErrorResult(-1, "smfcore.noValidStorage", "[{0}]料仓列表中未找到可用的料仓", new String[]{barcode.getBarcode()});
} else {
ReelLockPosInfo oldLockInfo = ReelLockPosUtil.getLockPosInfoByCode(barcode.getBarcode());
if (oldLockInfo != null) {
if (!oldLockInfo.getBarcode().equals(barcode.getBarcode())) {
ReelLockPosUtil.removeReelLockPosInfo(oldLockInfo.getBarcode());
log.info("清理锁定库位:库位号[" + oldLockInfo.getLockPosName() + "]上物料[" + oldLockInfo.getBarcode() + "]锁定的库位");
}
}
Storage storage = dataCache.getStorageById(pos.getStorageId());
ReelLockPosInfo reelLocInfo = new ReelLockPosInfo();
reelLocInfo.setBarcode(barcode.getBarcode());
reelLocInfo.setCid(storage.getCid());
reelLocInfo.setLockPosName(pos.getPosName());
reelLocInfo.setLockPosId(pos.getId());
reelLocInfo = ReelLockPosUtil.addReelLockPosInfo(reelLocInfo, cidList);
if (reelLocInfo == null) {
return ResultBean.newErrorResult(-1, "", "[" + barcode.getBarcode() + "]库位[" + reelLocInfo.getLockPosName() + "]已被锁定,暂停入库", new String[]{});
}
DataLog inTask = BoxUtil.generateInTask(pos, barcode, currentLoc);
taskService.addTaskToExecute(inTask);
resultMap.put("boxStr", barcode.getBarcode());
resultMap.put("posName", inTask.getPosName());
}
}
} catch (ValidateException e) {
log.info(boxStr + "解析失败:" + e.getMessage());
return ResultBean.newErrorResult(-1, e.getMsgKey(), e.getMessage(), e.getMsgParam());
}
return ResultBean.newOkResult(resultMap);
}
@ApiOperation("定时获取入库任务")
@RequestMapping("/service/store/agvBox/getPutInTask")
@AnonymousAccess
public ResultBean getPutInTask() {
List<CtuPutInTask> putInTaskList = new ArrayList<>();
List<DataLog> allTasks = taskService.getAllTasks();
for (DataLog dataLog : allTasks) {
if (dataLog.isPutInTask() && !dataLog.isFinished() && !dataLog.isCancel()){
CtuPutInTask putInTask = new CtuPutInTask();
putInTask.setStatus(dataLog.getStatus());
putInTask.setPosName(dataLog.getPosName());
putInTask.setBarcode(dataLog.getBarcode());
putInTask.setPutInLoc(dataLog.getCurrentLoc());
putInTaskList.add(putInTask);
}
}
return ResultBean.newOkResult(putInTaskList);
}
@ApiOperation("修改任务接口")
@RequestMapping(value = "/service/store/agvBox/updateLocInfo")
@AnonymousAccess
public synchronized ResultBean updateLocInfo(HttpServletRequest request) {
String rfid = request.getParameter("barcode");
String statusStr = request.getParameter("status");
String loc = request.getParameter("loc"); //工位
log.info("收到料盘[" + rfid + "]更新位置指令[" + statusStr + "]");
if (statusStr == null) {
return ResultBean.newErrorResult(301, "smfcore.updateLoc.status.empty", "状态不能为空");
}
if (rfid == null) {
return ResultBean.newErrorResult(302, "smfcore.updateLoc.barcode.empty", "条码不能为空");
}
DataLog opTask = null;
List<DataLog> allTasks = taskService.getAllTasks();
for (DataLog task : allTasks) {
if (rfid.startsWith(task.getBarcode())) {
if (!task.isCancel() && !task.isFinished()) {
opTask = task;
break;
}
}
}
if (opTask == null) {
return ResultBean.newErrorResult(303, "smfcore.task.notExist", "任务不存在");
}
if (opTask.isFinished()) {
return ResultBean.newErrorResult(304, "smfcore.task.hasEnd", "任务已完成");
}
statusStr = statusStr.toUpperCase();
log.info("更新料箱[" + rfid + "]的任务状态[" + opTask.getStatus() + "]为[" + statusStr + "]" + "目的地信息为:" + loc);
OP_STATUS updateStatus = OP_STATUS.valueOf(statusStr);
//如果是相同状态,则不执行
if (statusStr.equals(opTask.getStatus())) {
return ResultBean.newOkResult("");
}
if (updateStatus != null) {
opTask.setStatus(statusStr);
if (opTask.isCheckOutTask()) {
if (OP_STATUS.EXECUTING.name().equals(statusStr)) {
//在执行队列中
taskService.updateQueueTask(opTask);
} else {
taskService.moveTaskToFinished(opTask);
if (OP_STATUS.OUT_ON_AGV.name().equals(statusStr)) {
//从库位中取出,需要移到完成队列中,并且清理库存
BoxUtil.outFromPos(opTask);
}
taskService.updateFinishedTask(opTask);
if (OP_STATUS.FINISHED.name().equals(statusStr)) {
//已完成,从完成缓存中清除
taskService.removeFinishedTask(opTask);
}
}
} else {
opTask.setStatus(statusStr);
//入库任务
if (OP_STATUS.FINISHED.name().equals(statusStr)) {
taskService.moveTaskToFinished(opTask);
BoxUtil.intoPos(opTask);
ReelLockPosUtil.removeReelLockPosInfo(opTask.getBarcode());
} else {
taskService.updateQueueTask(opTask);
}
}
} else {
log.info(rfid + "更新状态时未找到状态:" + statusStr + "");
}
return ResultBean.newOkResult("");
}
@ApiOperation("定时获取出库库位列表")
@RequestMapping(value = "/service/store/agvBox/getOutTask")
@AnonymousAccess
public ResultBean getOutTask(HttpServletRequest request) {
Collection<DataLog> queueTasks = taskService.getQueueTasks();
queueTasks = queueTasks.stream().sorted(Comparator.comparing(DataLog::getPosName)).collect(Collectors.toList());
List<CtuCheckOutTask> ctuCheckOutTaskList = new ArrayList<>();
for (DataLog queueTask : queueTasks) {
if (queueTask.isWait() && queueTask.isCheckOutTask()) {
CtuCheckOutTask ctuCheckOutTask = new CtuCheckOutTask();
ctuCheckOutTask.setBarcode(queueTask.getBarcode());
ctuCheckOutTask.setPosName(queueTask.getPosName());
ctuCheckOutTask.setStatus(queueTask.getStatus());
String currentLoc = queueTask.getCurrentLoc();
if (StringUtils.isEmpty(currentLoc)) {
currentLoc = "RC2";
}
ctuCheckOutTask.setCheckOutLoc(currentLoc);
ctuCheckOutTaskList.add(ctuCheckOutTask);
}
}
return ResultBean.newOkResult(ctuCheckOutTaskList);
}
/**
* 工位状态定时上报
*/
@ApiOperation("工位状态定时上报")
@RequestMapping(value = "/service/store/agvBox/stationInfo")
@ResponseBody
@AnonymousAccess
public ResultBean stationInfo(HttpServletRequest request) {
for (int i = 1; i <= 5; i++) {
String stationName = "s" + i;
String rfid = request.getParameter(stationName);
if (StringUtils.isEmpty(rfid)) {
rfid = "";
}
Station station = StationCacheUtil.getStation(stationName);
if (station != null) {
String lastBox = station.getLastBox();
if (StringUtils.isNotEmpty(lastBox)) {
if (!rfid.equals(lastBox)) {
DataLog dataLog = null;
List<DataLog> allTasks = taskService.getAllTasks();
for (DataLog task : allTasks) {
if (task.isCheckOutTask() && !task.isCancel() && !task.isFinished()) {
if (lastBox.equals(task.getBarcode())){
dataLog = task;
break;
}
}
}
if (dataLog != null){
dataLog.setStatus(OP_STATUS.FINISHED.name());
taskService.updateFinishedTask(dataLog);
taskService.removeFinishedTask(dataLog);
}
}
}
} else {
station = new Station();
station.setName(stationName);
}
station.setLastBox(rfid);
station.setCurrentBox(rfid);
StationCacheUtil.updateStation(station);
}
return ResultBean.newOkResult(new ArrayList<>());
}
}
package com.neotel.smfcore.custom.aiqingzhiyin1643.util;
import com.neotel.smfcore.core.barcode.service.manager.IBarcodeManager;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.device.enums.OP;
import com.neotel.smfcore.core.device.enums.OP_STATUS;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.storage.service.manager.IStoragePosManager;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.util.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Slf4j
@Service
public class BoxUtil {
public static DataLog generateInTask(StoragePos pos, Barcode barcode,String currentLoc) {
Storage storage = dataCache.getStorageById(pos.getStorageId());
DataLog dataLog = new DataLog(storage,barcode,pos);
dataLog.setType(OP.PUT_IN);
dataLog.setCurrentLoc(currentLoc);
return dataLog;
}
public static void outFromPos(DataLog opTask) {
//从队列里面移除操作
taskService.removeQueueTask(opTask);
StoragePos storagePos = storagePosManager.get(opTask.getPosId());
Barcode barcode = storagePos.getBarcode();
if (barcode == null) {
log.warn("任务:" + opTask.getId() + " 仓位:" + opTask.getPosId() + " 的 Barcode 为null, 之前可能处理过,结束任务后直接返回");
return;
}
barcode = barcodeManager.get(barcode.getId());
if (barcode != null) {
//二维码状态
barcode.setUsed(true);
barcode.setUsedDate(new Date());
//仓位状态
barcode.setCheckOutDate(new Date(), "");
barcode.setPosName("");
barcodeManager.save(barcode);
}
storagePos.setBarcode(null);
storagePos.setUsed(false);
storagePosManager.save(storagePos);
log.info("出库完成,清空仓位: " + storagePos.getId() + "[" + storagePos.getPosName() + "]");
//更新缓存中的库存信息
dataCache.updateInventory(storagePos, barcode);
}
public static void intoPos(DataLog opTask) {
//已完成,加入库存,并且从完成队列中清除
StoragePos storagePos = storagePosManager.get(opTask.getPosId());
//二维码状态
Barcode barcode = barcodeManager.findByBarcode(opTask.getBarcode());
if (barcode != null) {
barcode.setUsedCount(barcode.getUsedCount() + 1);
barcode.setPutInTime(System.currentTimeMillis());
barcode.setInOpor("");
barcode.setCheckOutDate(null, "");
barcode.setPosName(opTask.getPosName());
storagePos.setBarcode(barcode);
dataCache.updateInventory(storagePos, barcode);
barcode = barcodeManager.save(barcode);
}
/**
* 仓位状态
*/
storagePos.setBarcode(barcode);
storagePos.setUsed(true);
storagePos.setCanCheckOutTime(System.currentTimeMillis());
storagePosManager.save(storagePos);
//更新缓存中的库存信息
opTask.setStatus(OP_STATUS.FINISHED.name());
taskService.updateFinishedTask(opTask);
taskService.removeFinishedTask(opTask);
}
private static DataCache dataCache;
@Autowired
private void setDataCache(DataCache cache){
BoxUtil.dataCache = cache;
}
private static TaskService taskService;
@Autowired
private void setTaskService(TaskService service){
BoxUtil.taskService = service;
}
private static IStoragePosManager storagePosManager;
@Autowired
private void setStoragePosManager(IStoragePosManager manager){
BoxUtil.storagePosManager = manager;
}
private static IBarcodeManager barcodeManager;
@Autowired
private void setBarcodeManager(IBarcodeManager manager){
BoxUtil.barcodeManager = manager;
}
}
package com.neotel.smfcore.custom.aiqingzhiyin1643.util;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.custom.aiqingzhiyin1643.bean.Station;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
@Slf4j
public class StationCacheUtil {
/**
* 工位缓存信息
*/
private static Map<String, Station> stationMap = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
log.info("开始工位缓存信息");
for (int i = 1; i <= 5; i++) {
String stationName = "s" + i;
Station station = dataCache.getCache(stationName);
if(station == null){
station = new Station();
station.setName(stationName);
}
stationMap.put(stationName,station);
dataCache.updateCache(stationName, station);
}
}
public static Station getStation(String stationName) {
return stationMap.get(stationName);
}
public static void updateStation(Station station) {
stationMap.put(station.getName(), station);
dataCache.updateCache(station.getName(), station);
}
private static DataCache dataCache;
@Autowired
public void setDataCache(DataCache dataCache) {
StationCacheUtil.dataCache = dataCache;
}
}
......@@ -2,15 +2,15 @@ server:
port: 8800
api:
name: 1568
name: 1643
inCheckUrl: #入库验证
outNotifyUrl: #出库通知
inNotifyUrl: http://10.96.31.231:8082/smfApi/pushWarehouseCompleteNoticeInfo #入库通知
inNotifyUrl: #入库通知
fetchOrderUrl: #获取工单
materialCountUrl: http://10.96.31.231:8082/smfApi/pushInvVerificationInfo #是否点料
postCountDataUrl: http://10.96.31.231:8082/smfApi/pushOrderResultUploadInfo #点料结果上传
shelfFullNotificationUrl: http://10.96.31.231:8082/smfApi/pushPickInstNoticeInfo #货架放满通知
orderNotifyUrl: 1
materialCountUrl: #是否点料
postCountDataUrl: #点料结果上传
shelfFullNotificationUrl: #货架放满通知
orderNotifyUrl:
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!