Commit 1f484b4b LN

增加普通料架类型NL,及扫码入库处理

1 个父辈 e48b9be2
......@@ -33,7 +33,7 @@ public class ResultBean<T> {
result.setMsg(MessageUtils.getText(msgKey, params, new Locale(SecurityUtils.getCurrentUserLanguage()), msg));
if (writeLog) {
log.info(msg);
log.info(result.getMsg());
}
return result;
}
......
package com.neotel.smfcore.core.device.bean;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
public class NLShelfOperateBean {
private String sessionId;
private long updateTime;
/**
* 当前操作库位信息
*/
private StoragePos opPos;
/**
* 需要关灯的库位
*/
private StoragePos posToClose;
/**
* 下一个库位的Id,用于自动推荐库位
*/
private String nextPosId;
public StoragePos getOpPos() {
return opPos;
}
public void setOpPos(StoragePos opPos) {
updateOpTime();
this.opPos = opPos;
}
public StoragePos getPosToClose() {
return posToClose;
}
public void setPosToClose(StoragePos posToClose) {
updateOpTime();
this.posToClose = posToClose;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
updateOpTime();
this.sessionId = sessionId;
}
public long getUpdateTime() {
return updateTime;
}
public void setUpdateTime(long updateTime) {
this.updateTime = updateTime;
}
private void updateOpTime(){
setUpdateTime(System.currentTimeMillis());
}
public String getNextPosId() {
return nextPosId;
}
public void setNextPosId(String nextPosId) {
this.nextPosId = nextPosId;
}
/**
* 1个小时没有任何操作,即认为超时
*/
public boolean timeOut(){
if(updateTime > 0){
return System.currentTimeMillis() - updateTime > 1 * 60 * 60 * 1000;
}
return false;
}
}
package com.neotel.smfcore.core.device.handler.impl;
import com.google.common.base.Strings;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.exception.ValidateException;
import com.neotel.smfcore.common.utils.SecurityUtils;
import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.device.api.IOpAuthApi;
import com.neotel.smfcore.core.device.bean.NLShelfOperateBean;
import com.neotel.smfcore.core.device.bean.StatusBean;
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.DevicesStatusUtil;
import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.security.TokenProvider;
import com.neotel.smfcore.security.annotation.AnonymousAccess;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Api(tags = "智能料架/亮灯料架/普通料架")
@RestController
@Slf4j
public class NLShelfHandler extends BaseDeviceHandler {
public NLShelfHandler(List<IOpAuthApi> apiList) {
super(apiList);
}
//上一次入库的库位,用于扫下一条码时灭灯
private static Map<String,NLShelfOperateBean> shelfPutInBeanMap = new ConcurrentHashMap<>();
@Autowired
private TokenProvider tokenProvider;
@Autowired
private TaskService taskService;
@Override
public StatusBean handleClientRequest(StatusBean statusBean, HttpServletRequest request) {
handleMsg(statusBean);
statusBean = saveAlarmAndHumidity(statusBean);
if(statusBean != null){
Map<String, String> opMap = DevicesStatusUtil.getAndRemoveOp(statusBean.getCid());
statusBean.addOp(opMap);
}
return statusBean;
}
/**
* 关闭上一个亮灯库位
*/
private void closeLastPos(String sessionId){
NLShelfOperateBean operateBean= shelfPutInBeanMap.get(sessionId);
if(operateBean != null){
StoragePos posToClose = operateBean.getPosToClose();
if(posToClose != null){
log.info(sessionId + "关闭上一个操作库位:"+ posToClose.getPosName());
operateBean.setPosToClose(null);
shelfPutInBeanMap.put(sessionId, operateBean);
opPosLight("close", posToClose, "");
}
}
for (NLShelfOperateBean shelfOperateBean : shelfPutInBeanMap.values()) {
if(shelfOperateBean.timeOut()){
String removedSessionId = shelfOperateBean.getSessionId();
log.info("料架入库缓存["+shelfOperateBean.getSessionId()+"]长时间未操作,清理缓存");
shelfPutInBeanMap.remove(removedSessionId);
}
}
}
private NLShelfOperateBean getOperateBean(String sessionId){
NLShelfOperateBean operateBean= shelfPutInBeanMap.get(sessionId);
if(operateBean == null){
operateBean = new NLShelfOperateBean();
operateBean.setSessionId(sessionId);
shelfPutInBeanMap.put(sessionId, operateBean);
}
return operateBean;
}
/**
* 操作库位灯(开灯,或关灯)
* @param opKey
* @param pos
* @param colorStr
*/
private void opPosLight(String opKey, StoragePos pos, String colorStr){
String opStr = pos.getPosName();
if(!Strings.isNullOrEmpty(colorStr)){
opStr =opStr+ "=" + colorStr;
}
Storage storage = dataCache.getStorageById(pos.getStorageId());
DevicesStatusUtil.appendOp(storage.getCid(), opKey , opStr);
//log.info("操作库位["+pos.getPosName()+"]" + opKey + " : " + opStr);
}
/**
* 开灯, 等30秒后关闭
*/
private void openAndCloseLights(final String sessionId, final StoragePos pos, String color, final long delayCloseTime){
if(pos == null){
return;
}
final Storage storage = dataCache.getStorageById(pos.getStorageId());
final String cid = storage.getCid();
String lightOnStr = pos.getPosName() + "=" + color;
DevicesStatusUtil.appendOp(cid,"open", lightOnStr);
log.info("点亮库位:" + lightOnStr);
//30秒后灭灯
Thread closeTask = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(delayCloseTime);
log.info("自动关闭库位:"+ pos.getPosName());
opPosLight("close", pos, "");
}catch (Exception e){
}
}
});
closeTask.start();
}
/**
* 检查料盘是否可以放入库位中
*/
private void canBarcodePutInPos(StoragePos pos, Barcode barcode) throws ValidateException {
if(pos.getBarcode() != null){
throw new ValidateException("smfcode.shelf.nextPos.hasReel", "库位[{0}]已有物料,请重新扫描库位码",new String[]{pos.getPosName()});
}
Collection<String> excludePosIds = taskService.excludePosIds();
if(excludePosIds.contains(pos.getId())){
throw new ValidateException("smfcode.shelf.nextPos.hasTask", "库位[{0}]已有任务,请重新扫描库位码",new String[]{pos.getPosName()});
}
// if(barcode != null){
// Storage storage = dataCache.getStorageById(pos.getStorageId());
// ArrayList<Storage> storageList = Lists.newArrayList(storage);
// taskService.verifyBarcodePutIn(storageList,barcode);
//
// if(!storage.canPutInPos(barcode.getPlateSize(),barcode.getHeight(), pos.getW(), pos.getH())){
// String reelSize = barcode.getPlateSize() + "x" + barcode.getHeight();
// String posSize = pos.getW() + "x" + pos.getH();
// throw new ValidateException("smfcode.shelf.nextPos.wrongSize","库位["+pos.getPosName()+"]尺寸["+posSize+"]与料盘尺寸["+reelSize+"]不符,请重新扫描库位码",new String[]{pos.getPosName(),posSize, reelSize});
// }
// }
}
/**
* 扫码
*/
@ApiOperation("扫码入库")
@PostMapping("/api/nlShelf/codeIn")
@PreAuthorize("@el.check('NLShelf:putIn')")
public ResultBean codeIn(@RequestBody Map<String, String> mapValues,HttpServletRequest request) {
String code = mapValues.get("code");
String groupId = mapValues.get("group");
String storageId = mapValues.get("storageId");
// String sessionId = request.getSession().getId();
String token = tokenProvider.getToken(request);
String ptoken=token.substring(token.length()-10);
String loginUser = SecurityUtils.getLoginUsername();
if (ObjectUtils.isEmpty(code)) {
throw new ValidateException("smfcode.valueCanotNull", "{0}不能为空", new String[]{"code"});
}
if (groupId == null && storageId == null) {
throw new ValidateException("smfcode.valueCanotNull", "{0}不能为空", new String[]{"group"});
}
Storage currentStorage = null;
if (groupId == null && storageId != null) {
currentStorage = dataCache.getStorageById(storageId);
if (currentStorage == null || !currentStorage.isNLShelf()) {
return ResultBean.newErrorResult(1, "smfcode.shelf.notFound", "未找到料架");
}
if (currentStorage != null) {
groupId = currentStorage.getGroupId();
}
}
if (groupId != null && groupId.equals("-1")) {
groupId = "";
}
int delayCloseTime = 30000;
String putInColor = "green";
StoragePos pos = storagePosManager.getByPosName(code);
if (pos != null) {
//判断库位是否是对应设备或者租
if (currentStorage != null) {
if ( ! pos.getStorageId().equals(storageId)) {
return ResultBean.newErrorResult(1, "smfcode.shelf.pos.notInStorage", "料架[{0}]中未找到库位[{1}]", new String[]{currentStorage.getName(), pos.getPosName()});
}
} else {
currentStorage = dataCache.getStorageById(pos.getStorageId());
if (currentStorage == null || !currentStorage.isNLShelf()) {
return ResultBean.newErrorResult(1, "smfcode.shelf.notFound", "未找到料架");
}
if (currentStorage.getGroupId() != groupId) {
return ResultBean.newErrorResult(1, "smfcode.shelf.pos.notInGroup", "组中未找到库位[{0}]", new String[]{pos.getPosName()});
}
}
//扫描的为库位条码,先关掉上一个库位灯, 当前库位中没有物料的话点亮库位灯
closeLastPos(token);
if (pos.getBarcode() != null) {
return ResultBean.newErrorResult(1, "smfcode.shelf.msg.hasReel", "库位中[{0}]已有物料", new String[]{pos.getPosName()});
} else {
//设置上一个入库操作库位
openAndCloseLights(token, pos, putInColor, delayCloseTime);
NLShelfOperateBean operateBean = getOperateBean(token);
operateBean.setOpPos(pos);
operateBean.setPosToClose(pos);
operateBean.setNextPosId(null);
shelfPutInBeanMap.put(token, operateBean);
log.info(ptoken + ":库位[" + pos.getPosName() + "]操作成功,请扫描要放入的物料");
return ResultBean.newOkResult("smfcode.shelf.msg.tipScanReel", "库位[" + pos.getPosName() + "]操作成功,请扫描要放入的物料", new String[]{pos.getPosName()},"");
}
} else {
//扫的是物料条码
Barcode barcode = codeResolve.resolveOneValideBarcode("=1x1=" + code);
Date expireDate = barcode.getExpireDate();
if (expireDate != null) {
if (System.currentTimeMillis() > expireDate.getTime()) {
throw new ValidateException("smfcore.error.barcode.expired", "物料已过期,无法入库.");
}
}
try {
// if(barcode.getPlateSize() == 1){
// //长宽为1的需要弹框设置尺寸
// throw new ValidateException("error.barcode.errorSize","条码未设置尺寸");
// }
long now = System.currentTimeMillis();
//5秒内同一个条码忽略
Date usedDate = barcode.getUsedDate();
if (usedDate != null && now - usedDate.getTime() < 5000) {
return ResultBean.newErrorResult(1, "smfcode.shelf.msg.fastop", "条码操作频繁,请稍后再试");
}
StoragePos inPos = storagePosManager.getByBarcodeId(barcode.getId());
if (inPos != null) {
// String sourceName = request.getParameter("sourceName");
Collection<DataLog> allTasks = taskService.getQueueTasks();
for (DataLog task : allTasks) {
if (task.isCheckOutTask() && task.getBarcode() != null && task.getBarcode().equals(barcode.getBarcode())) {
// if(!sourceName.equalsIgnoreCase("All") && !sourceName.equals(task.getSourceName())){
// return ResultBean.newErrorResult(1,"smfcode.shelf.error.orderError","任务与指定工单[{0}]不一致",new String[]{sourceName} );
// }
taskService.addTaskToFinished(inPos, null, loginUser);
opPosLight("close", inPos, null);
log.info(barcode.getBarcode() + " 出库完成, 库位[" + inPos.getPosName() + "]灭灯");
return ResultBean.newErrorResult(1, "smfcode.shelf.msg.outConfirm", "出库完成, 库位[{0}]灭灯", new String[]{inPos.getPosName()} );
}
}
return ResultBean.newErrorResult(1, "smfcode.shelf.msg.noTask", "操作失败,已在库位[{0}]中,未找到对应的出库任务", new String[]{inPos.getPosName()});
}
//入库
NLShelfOperateBean operateBean = getOperateBean(token);
StoragePos opPos = operateBean.getOpPos();
if (opPos == null) {
//未扫描库位
String operatePosId = operateBean.getNextPosId();
if (Strings.isNullOrEmpty(operatePosId)) {
log.info(ptoken + ":条码[" +code+ "],请先扫描库位码");
return ResultBean.newErrorResult(1, "smfcode.shelf.msg.scanPos", "请先扫描库位码");
} else {
//有下一个库位
StoragePos currentPos = storagePosManager.get(String.valueOf(operatePosId));
if (currentPos != null) {
canBarcodePutInPos(currentPos, barcode);
//验证通过,先灭上一个库位灯
closeLastPos(token);
//打开下一个库位灯
openAndCloseLights(token, currentPos, putInColor, delayCloseTime);
taskService.addTaskToFinished(currentPos, barcode, loginUser);
operateBean.setOpPos(null);
operateBean.setPosToClose(currentPos);
Collection<String> operatingPosIds = taskService.excludePosIds();
StoragePos nextPos = storagePosManager.autoFindNextEmptyPos(currentStorage, operatingPosIds, currentPos);
String nextPosId = nextPos == null ? "null" : nextPos.getId() + "[" + nextPos.getPosName() + "]";
//
operateBean.setNextPosId(null);
if (nextPos != null) {
operateBean.setNextPosId(nextPos.getId());
}
shelfPutInBeanMap.put(token, operateBean);
log.info(ptoken + ":条码[" + code + "]入库操作成功,请放入库位 [" + currentPos.getPosName() + "],下一个库位号:" + nextPosId);
return ResultBean.newOkResult("smfcode.shelf.msg.inOk", "操作成功,请放入库位[" + currentPos.getPosName() + "]", new String[]{currentPos.getPosName()}, "");
}
}
} else {
//已扫过库位
canBarcodePutInPos(opPos, barcode);
log.info(barcode.getPartNumber() + " [ " + barcode.getBarcode() + " ] " + "入库到:" + opPos.getPosName());
opPos.setCanCheckOutTime(System.currentTimeMillis() + delayCloseTime);
taskService.addTaskToFinished(opPos, barcode, loginUser);
//放入后不需要灭灯,也不需要亮灯,等下一个条码扫到时再灭灯
operateBean.setOpPos(null);
// Long nextPosId = Long.valueOf(opPos.getId()) + 1;
// operateBean.setNextPosId(String.valueOf(nextPosId));
// shelfPutInBeanMap.put(token, operateBean);
Collection<String> operatingPosIds = taskService.excludePosIds();
StoragePos nextPos = storagePosManager.autoFindNextEmptyPos(currentStorage, operatingPosIds, opPos);
String nextPosId = nextPos == null ? "null" : nextPos.getId() + "[" + nextPos.getPosName() + "]";
//
operateBean.setNextPosId(null);
if (nextPos != null) {
operateBean.setNextPosId(nextPos.getId());
}
shelfPutInBeanMap.put(token, operateBean);
log.info(ptoken + ":条码["+code+"]入库操作成功,请放入库位 ["+opPos.getPosName()+"],下一个库位号:" + nextPosId);
return ResultBean.newOkResult("smfcode.shelf.msg.inOk", "操作成功,请放入库位["+opPos.getPosName()+"]", new String[]{opPos.getPosName()},"");
}
} catch (ValidateException e) {
log.error("Error:" + e.getMessage());
return ResultBean.newErrorResult(1, e.getMessage(), e.getMessage(), e.getMsgParam());
}
}
return ResultBean.newOkResult("");
}
/**
* 获取料架的库位占用情况
*/
@RequestMapping("/service/store/nlShelf/hasReelPosList")
@ResponseBody
@AnonymousAccess
public ResultBean checkAll(HttpServletRequest request){
String cid = request.getParameter("cid");
Storage storage = dataCache.getStorage(cid);
List<StoragePos> allPos = storagePosManager.findNotEmptyByStorageId(storage.getId());
List<String> posList = new ArrayList<>();
for (StoragePos pos : allPos) {
posList.add(pos.getPosName());
}
return ResultBean.newOkResult(posList);
}
@Override
public DeviceType getDeviceType() {
return DeviceType.NL;
}
}
......@@ -100,7 +100,12 @@ public enum DeviceType {
SMD_XLR("storage.type.smdXlr"),
/**
* 15 (默认料仓)
* 16 智能料架/亮灯料架/普通料架 NL
*/
NL("storage.type.nl"),
/**
* 17 (默认料仓)
*/
DEFAULT("storage.type.default")
;
......
......@@ -199,8 +199,13 @@ public class MaterialController {
if (posIds == null || posIds.size() <= 0) {
throw new ValidateException("smfcode.valueCanotNull", "{0}不能为空", new String[]{"ID"});
}
String labelName="";
if (ObjectUtil.isEmpty(labelId)) {
labelId = "";
}else
{
Label label=labelManager.get(labelId);
labelName=label.getLabelName();
}
String[] array = posIds.toArray(new String[posIds.size()]);
storagePosManager.updatePosLabel(array, labelId);
......@@ -231,7 +236,7 @@ public class MaterialController {
return ResultBean.newOkResult("smfcode.manualOut.ok", "手动出库成功", code);
} catch (Exception e) {
return ResultBean.newOkResult("smfcore.error", "出错{0}", new String[]{e.getMessage()}, code);
return ResultBean.newErrorResult(1,"smfcore.error", "出错{0}", new String[]{e.getMessage()}, true);
}
} else {
throw new ValidateException("smfcode.manualOut.notFound", "仓库中未找到料盘信息");
......
......@@ -53,4 +53,6 @@ public interface IStoragePosManager extends IBaseManager<StoragePos> {
void updatePosLabel(String[] posIds, String labelId);
void clearLockPos(String lockId);
StoragePos autoFindNextEmptyPos(Storage storage, Collection<String> excludePosIds,StoragePos currentPos);
}
......@@ -407,4 +407,38 @@ public class StoragePosManagerImpl implements IStoragePosManager {
storagePosDao.updateMulti(query, Update.update("barcode.lockId",""));
}
@Override
public StoragePos autoFindNextEmptyPos(Storage storage, Collection<String> excludePosIds,StoragePos currentPos) {
Criteria c = Criteria.where("storageId").is(storage.getId());
// c.and("priority").lt(currentPos.getPriority());
COMPATIBLE_TYPE compatibleType = storage.getCompatibleType();
if (compatibleType == COMPATIBLE_TYPE.EXACT_MATCH) {//完全匹配
c = c.and("w").is(currentPos.getW()).and("h").is(currentPos.getH());
} else if (compatibleType == COMPATIBLE_TYPE.FULLY_COMPATIBLE) {//同厚度兼容
c = c.and("w").gte(currentPos.getW()).and("h").gte(currentPos.getH());//除7寸外,完全兼容
} else if (compatibleType == COMPATIBLE_TYPE.SIZE_COMPATIBLE) {//同尺寸兼容
c = c.and("w").is(currentPos.getW()).and("h").gte(currentPos.getH());//宽度等于料盘宽度,高度大于等于料盘高度
}
c = c.and("enabled").is(true)//可用
.and("used").is(false);//未使用
//去除的仓位
if (excludePosIds != null && !excludePosIds.isEmpty()) {
c = c.and("id").nin(excludePosIds);
}
Query query = new Query(c);
String msg = "";
// if (lastPosId == null || lastPosId.equals("")) {
//优先放入最合适的位置(根据尺寸),相同尺寸按优先级排序
query.with(Sort.by(Sort.Direction.ASC, "w").and(Sort.by(Sort.Direction.ASC, "h")).and(Sort.by(Sort.Direction.DESC, "priority")));
StoragePos pos = storagePosDao.findOne(query);
return pos;
}
}
......@@ -120,7 +120,12 @@ public class Storage extends BasePo implements Serializable {
public boolean isCodeShelf() {
return DeviceType.CODESHELF.name().equals(type);
}
/**
* 是否是普通料架
*/
public boolean isNLShelf() {
return DeviceType.NL.name().equals(type);
}
/**
* 是否是垂直货柜
*/
......
......@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by sunke on 2021/8/4.
......@@ -56,6 +57,9 @@ public class DevicesStatusUtil {
}
/**
* 主要用来存储感应料架库位报警信息
*/
private static Map<String, List<List<String>>> deviceDataMap = Maps.newConcurrentMap();
public static void updateDeviceData(String cid, List<List<String>> data) {
......@@ -66,4 +70,55 @@ public class DevicesStatusUtil {
return deviceDataMap.get(cid);
}
/**
*存储料仓 出入库的一些操作,KEY=cid
*/
private static Map<String,Map<String, String>> storageOpMap = new ConcurrentHashMap<>();
/**
* 添加操作
* @param cid
* @param opKey
* @param opValue
*/
public static void addOp(String cid, String opKey, String opValue){
if(opValue == null){
return;
}
Map<String, String> opMap = storageOpMap.get(cid);
if(opMap == null){
opMap = new ConcurrentHashMap<>();
}
opMap.put(opKey, opValue);
storageOpMap.put(cid, opMap);
}
/**
* 追加操作
* @param cid
* @param opKey
* @param opValue
*/
public static void appendOp(String cid, String opKey, String opValue){
Map<String, String> opMap = storageOpMap.get(cid);
if(opMap == null){
opMap = new ConcurrentHashMap<>();
}
String value = opMap.get(opKey);
if(value != null){
value = value + "|" + opValue;
}else{
value = opValue;
}
opMap.put(opKey, value);
storageOpMap.put(cid, opMap);
}
public static Map<String,String> getAndRemoveOp(String cid){
Map<String, String> opMap = storageOpMap.get(cid);
opMap.remove(cid);
return opMap;
}
}
......@@ -783,7 +783,7 @@ public class TaskService {
task.setPartNumber(barcode.getPartNumber());
task.setBarcode(barcode.getBarcode());
task.setNum(barcode.getInitialAmount());
task.setNum(barcode.getAmount());
dataCache.updateInventory(pos, barcode);
//dataCache.updateStorage(task.getCid());
......
......@@ -184,7 +184,7 @@ public class HellaServiceHandler extends IoHandlerAdapter implements ITaskListen
String groupId = requestCommand.getGroupId();
if(respCommand.isOkResp()){
ResultBean okResult = ResultBean.newOkResult("smfcore.loadMaterialFinished","loading material is finished: {0}" ,new String[]{messageText});
ResultBean okResult = ResultBean.newOkResult("smfcore.loadMaterialFinished","loading material is finished: {0}" ,new String[]{messageText},"");
WebSocketServer.sendGroupMsg(groupId,new SocketMsg(okResult.getMsg(), MsgType.INFO));
}else{
ResultBean ngResult = ResultBean.newErrorResult(Integer.valueOf(returnCode),"smfcore.loadMaterialFailed","loading material failed:{0}",new String[]{messageText} );
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!