Commit 373409d1 sunke

设备端接口

1 个父辈 1f05edb3
...@@ -33,6 +33,20 @@ public enum OP_STATUS { ...@@ -33,6 +33,20 @@ public enum OP_STATUS {
/** /**
* 已结束 * 已结束
*/ */
END END,
/**
* 任务状态
* 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
; ;
} }
...@@ -117,7 +117,12 @@ public enum DeviceType { ...@@ -117,7 +117,12 @@ public enum DeviceType {
/** /**
* 18 (默认料仓) * 18 (默认料仓)
*/ */
DEFAULT("storage.type.default") DEFAULT("storage.type.default"),
/**
* 19 (AGV BOX)
*/
AGV_BOX("storage.type.agvBox")
; ;
private String key; private String key;
......
...@@ -30,6 +30,7 @@ import org.springframework.stereotype.Service; ...@@ -30,6 +30,7 @@ import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
@Service @Service
@Slf4j @Slf4j
...@@ -222,7 +223,11 @@ public class StoragePosManagerImpl implements IStoragePosManager { ...@@ -222,7 +223,11 @@ public class StoragePosManagerImpl implements IStoragePosManager {
@Override @Override
public StoragePos getByBarcode(String barcode){ public StoragePos getByBarcode(String barcode){
return storagePosDao.findOneByCondition(new String[]{"barcode.barcode"}, new String[]{barcode}); List<Criteria> orCriterialList = Lists.newArrayList();
orCriterialList.add(Criteria.where("barcode.barcode").is(barcode));
orCriterialList.add(Criteria.where("barcode.subCodeList.barcode").is(barcode));
Criteria c = new Criteria().orOperator(orCriterialList);
return storagePosDao.findOne(new Query(c));
} }
@Override @Override
......
...@@ -89,6 +89,13 @@ public class Storage extends BasePo implements Serializable { ...@@ -89,6 +89,13 @@ public class Storage extends BasePo implements Serializable {
} }
/** /**
* 是否是某种类型的料仓
*/
public boolean isStorage(DeviceType deviceType){
return deviceType.name().equals(type);
}
/**
* 是否是单台自动仓 * 是否是单台自动仓
*/ */
public boolean isAuto(){ public boolean isAuto(){
......
...@@ -252,6 +252,10 @@ public class DataLog extends BasePo implements Serializable { ...@@ -252,6 +252,10 @@ public class DataLog extends BasePo implements Serializable {
return OP_STATUS.END.name().equals(status); return OP_STATUS.END.name().equals(status);
} }
public boolean isTaskInStatus(OP_STATUS taskStatus){
return taskStatus.name().equals(status);
}
/** /**
* 是否是入库任务 * 是否是入库任务
...@@ -274,6 +278,7 @@ public class DataLog extends BasePo implements Serializable { ...@@ -274,6 +278,7 @@ public class DataLog extends BasePo implements Serializable {
return OP.MIX == type; return OP.MIX == type;
} }
/** /**
* 是否是回温放料任务 * 是否是回温放料任务
*/ */
......
...@@ -831,7 +831,7 @@ public class TaskService { ...@@ -831,7 +831,7 @@ public class TaskService {
/** /**
* 出库完成 * 出库完成
*/ */
private void checkoutFinished(DataLog task) throws ValidateException { public void checkoutFinished(DataLog task) throws ValidateException {
boolean isCancelTask = task.isCancel(); boolean isCancelTask = task.isCancel();
StoragePos storagePos = storagePosManager.get(task.getPosId()); StoragePos storagePos = storagePosManager.get(task.getPosId());
......
package com.neotel.smfcore.custom.lizhen.rest;
import com.neotel.smfcore.common.bean.ResultBean;
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_STATUS;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.storage.enums.DeviceType;
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 com.neotel.smfcore.custom.lizhen.bean.Station;
import com.neotel.smfcore.custom.lizhen.util.StationCacheUtil;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
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.concurrent.ConcurrentHashMap;
/**
* 设备通信(AGV料仓)
*/
@Slf4j
@RestController
public class AgvBoxDeviceClientController {
@Autowired
private DataCache dataCache;
@Autowired
private TaskService taskService;
@Autowired
private IBarcodeManager barcodeManager;
@Autowired
private IStoragePosManager storagePosManager;
/**
* 料箱锁定的目的地
*/
private Map<String, String> lockRfidTarget = new ConcurrentHashMap<>();
/**
* 工位状态定时上报
*/
@ApiOperation("工位状态定时上报")
@PostMapping(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);
StationCacheUtil.updateCurrentRfid(stationName, rfid);
}
//"needInStation": ["s1","s2"]//需要线体升起阻挡进行入库的工位
List<String> needInStationList = new ArrayList<>();
Collection<DataLog> queueTasks = taskService.getQueueTasks();
for (DataLog queueTask : queueTasks) {
if (queueTask.isPutInTask() && queueTask.isWait()) {
Storage storage = dataCache.getStorageById(queueTask.getStorageId());
if (storage.isStorage(DeviceType.AGV_BOX)) {
String barcode = queueTask.getBarcode();
String station = getStationByRfid(barcode);
//任务的条码与工位的箱号一致,需要线体升起阻挡进行入库
if (!station.isEmpty()) {
needInStationList.add(station);
}
}
}
}
Map<String, List<String>> dataMap = new HashMap<>();
dataMap.put("needInStation", needInStationList);
return ResultBean.newOkResult(dataMap);
}
/**
* 根据料箱RFID获取工位信息
*/
private String getStationByRfid(String rfid) {
String prefixRfid = rfid + "-";
for (Station station : StationCacheUtil.getAllStations()) {
String stationRfid = station.getCurrentRfid();
if (stationRfid.startsWith(prefixRfid)) {
return station.getName();
}
}
return "";
}
/**
* 定时获取出库库位列表
*
* @param request
* @return
*/
@RequestMapping(value = "/service/store/agvBox/getOutTask")
@ResponseBody
@AnonymousAccess
public ResultBean getOutTask(HttpServletRequest request) {
Collection<DataLog> queueTasks = taskService.getQueueTasks();
List<Map<String, String>> taskToSend = new ArrayList<>();
for (DataLog queueTask : queueTasks) {
if (queueTask.isCheckOutTask()) {
if (queueTask.isWait() || queueTask.isExecuting()) {
Storage storage = dataCache.getStorageById(queueTask.getStorageId());
if (storage.isStorage(DeviceType.AGV_BOX)) {
Map<String, String> taskData = new HashMap<>();
taskData.put("barcode", queueTask.getBarcode());
taskData.put("slotCode", queueTask.getPosName());
taskData.put("status", queueTask.getStatus());
taskToSend.add(taskData);
}
}
}
}
return ResultBean.newOkResult(taskToSend);
}
/**
* 根据料箱条码获取目的地信息
*
* @param request
* @return
*/
@RequestMapping(value = "/service/store/agvBox/getTarget")
@ResponseBody
@AnonymousAccess
public ResultBean getTarget(HttpServletRequest request) {
String rfid = request.getParameter("barcode");
try {
//"barcode": "CS001"
DataLog opTask = null;
Collection<DataLog> tasks = taskService.getAllTasks();
for (DataLog task : tasks) {
if (!task.isFinished()) {
Storage storage = dataCache.getStorageById(task.getStorageId());
if (storage.isStorage(DeviceType.AGV_BOX)) {
if (isSameBarcodeTask(rfid, task)) {
opTask = task;
}
}
}
}
String target = "0";
String barcode = "";
//"target": "1",//0=工位已有箱子;s1=工位1;s2=工位2;s3=工位3;s4=工位4;s5=工位5;其他值表示入库的目标库位
if (opTask != null) {
barcode = opTask.getBarcode();
if (opTask.isPutInTask()) {
target = opTask.getPosName();
} else {
String loc = lockRfidTarget.get(opTask.getBarcode());
if (loc != null) {
target = loc;
} else {
for (Station station : StationCacheUtil.getAllStations()) {
if (lockRfidTarget.get(station.getName()) == null) {
//工位未分配过库位
log.info("分配料箱[" + opTask.getBarcode() + "]到" + station.getName());
lockRfidTarget.put(opTask.getBarcode(), station.getName());
target = station.getName();
break;
}
}
}
}
}
Map<String, String> taskData = new HashMap<>();
taskData.put("barcode", barcode);
taskData.put("slotCode", target);
return ResultBean.newOkResult(taskData);
} catch (Exception e) {
return ResultBean.newErrorResult(2004, "smfcore.agvBox.getTarget.fail", "获取" + rfid + "目的地失败");
}
}
/**
* 更新任务状态
* EXECUTING=已发送到AGV
* INLINE=料箱放上线体(入库时人员推上线体, 出库时指AGV放上线体),
* INAGV=料箱放上AGV(入库时AGV从线体上抓起料箱;出库时AGV从库位取走料箱),
* FINISHED=任务完成(入库时AGV将料箱放入库位;出库时料箱到达工位)
*/
@PostMapping(value = "/service/store/agvBox/updateLocInfo")
@ResponseBody
@AnonymousAccess
public synchronized ResultBean updateLocInfo(HttpServletRequest request) {
String rfid = request.getParameter("barcode");
String statusStr = request.getParameter("status");
log.debug("收到料盘[" + 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())) {
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 + "]");
OP_STATUS oldStatus = OP_STATUS.valueOf(opTask.getStatus());
OP_STATUS updateStatus = OP_STATUS.valueOf(statusStr);
/**
* 任务状态
* EXECUTING=已发送到AGV
* IN_ON_LINE=入库时人员将料箱推上线体,
* IN_ON_AGV=入库时AGV从线体上抓起料箱
* OUT_ON_LINE=出库时AGV将料箱放上线体,
* OUT_ON_AGV=出库时AGV将料箱从库位中取出
* FINISHED=任务完成(入库时AGV将料箱放入库位;出库时料箱到达工位)
*/
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)) {
//从库位中取出,需要移到完成队列中,并且清理库存
outFromPos(opTask);
}
taskService.updateFinishedTask(opTask);
if (OP_STATUS.FINISHED.name().equals(statusStr)) {
lockRfidTarget.remove(opTask.getBarcode());
//已完成,从完成缓存中清除
taskService.removeFinishedTask(opTask);
}
}
} else {
opTask.setStatus(statusStr);
//入库任务
if (OP_STATUS.FINISHED.name().equals(statusStr)) {
lockRfidTarget.remove(opTask.getBarcode());
intoPos(opTask);
} else {
taskService.updateQueueTask(opTask);
}
}
} else {
log.info(rfid + "更新状态时未找到状态:" + statusStr + "");
}
return ResultBean.newOkResult("");
}
/**
* 料箱从仓位中取出
*/
private 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());
barcodeManager.save(barcode);
}
/**
* 仓位状态
*/
storagePos.setBarcode(barcode);
storagePos.setUsed(true);
storagePos.setCanCheckOutTime(System.currentTimeMillis());
storagePosManager.save(storagePos);
if (barcode != null) {
dataCache.updateInventory(storagePos, barcode);
//需要更新料箱中物料的库存
}
//更新缓存中的库存信息
opTask.setStatus(OP_STATUS.FINISHED.name());
taskService.updateFinishedTask(opTask);
taskService.removeFinishedTask(opTask);
}
/**
* 料箱放入仓位
*/
private 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);
}
/**
* 是否是同一个箱子的任务
*
* @param rfid 客户端上传的RFID,值为CS001-A或CS001-B这种形式,只需要验证前缀是否相同
* @param task 任务
* @return
*/
private boolean isSameBarcodeTask(String rfid, DataLog task) {
if (rfid == null) {
return false;
}
String taskBarcode = task.getBarcode();
if (rfid.startsWith(taskBarcode)) {
return true;
}
return false;
}
}
package com.neotel.smfcore.custom.lizhen.util; package com.neotel.smfcore.custom.lizhen.util;
import cn.hutool.core.util.ObjectUtil;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.device.util.DataCache; import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.inList.bean.ItemReelInfo;
import com.neotel.smfcore.core.inList.enums.INLIST_STATUS;
import com.neotel.smfcore.core.inList.service.manager.IInListItemManager;
import com.neotel.smfcore.core.inList.service.manager.IInListManager;
import com.neotel.smfcore.core.inList.service.po.InList;
import com.neotel.smfcore.core.inList.service.po.InListItem;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.custom.lizhen.bean.Station; import com.neotel.smfcore.custom.lizhen.bean.Station;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.Date; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
...@@ -66,12 +53,21 @@ public class StationCacheUtil { ...@@ -66,12 +53,21 @@ public class StationCacheUtil {
} }
Station station = getStation(stationName); Station station = getStation(stationName);
if(station != null){ if(station != null){
station.setCurrentRfid(rfid); String oldRfid = station.getCurrentRfid();
updateStation(station); if (!oldRfid.equals(rfid)) {
log.info("工位" + stationName + " 切换料箱,当前料箱:" + rfid + ",上一料箱:" + oldRfid);
station.setCurrentRfid(rfid);
updateStation(station);
}
} }
} }
/**
* 获取所有工位的缓存信息
*/
public static Collection<Station> getAllStations(){
return stationMap.values();
}
/** /**
* 获取工位信息 * 获取工位信息
......
...@@ -3,7 +3,7 @@ server: ...@@ -3,7 +3,7 @@ server:
api: api:
#name: Neotel #name: Neotel
#inCheckUrl: DaLu #inCheckUrl:
#outNotifyUrl: https://matlabel-tool.com:4434/SMD_BOXAPI/OutBox #outNotifyUrl: https://matlabel-tool.com:4434/SMD_BOXAPI/OutBox
#inNotifyUrl: https://matlabel-tool.com:4434/SMD_BOXAPI/InBox #inNotifyUrl: https://matlabel-tool.com:4434/SMD_BOXAPI/InBox
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!