Commit 8e65d489 LN

Merge remote-tracking branch 'origin/master'

2 个父辈 969c0a43 ea5f0a85
正在显示 35 个修改的文件 包含 1434 行增加28 行删除
...@@ -235,6 +235,7 @@ public class MenuInit { ...@@ -235,6 +235,7 @@ public class MenuInit {
Menu poutSet = Menu.CreatePMenu("设置", 13, "system","system",null); Menu poutSet = Menu.CreatePMenu("设置", 13, "system","system",null);
addDefaultFunctionMenu(101, poutSet, "设备管理", "bunker", "storage/storage/index", "database",DEFAULT_SHOW_MENU); addDefaultFunctionMenu(101, poutSet, "设备管理", "bunker", "storage/storage/index", "database",DEFAULT_SHOW_MENU);
addDefaultFunctionMenu(102,poutSet, "库位管理", "storagePos", "storagePos/storagePos/index", "tree-table",DEFAULT_SHOW_MENU); addDefaultFunctionMenu(102,poutSet, "库位管理", "storagePos", "storagePos/storagePos/index", "tree-table",DEFAULT_SHOW_MENU);
addDefaultFunctionMenu(103,poutSet, "合并库位", "selectDevice", "neolight/selectDevice/index", "selectDevice");
addDefaultFunctionMenu(103, poutSet, "菜单管理", "menu", "system/menu/index", "menu"); addDefaultFunctionMenu(103, poutSet, "菜单管理", "menu", "system/menu/index", "menu");
// Menu sysSetting = new Menu(, "barcode", "条码设置", "barcodeSetting", "system/barcodeSetting/index", "database"); // Menu sysSetting = new Menu(, "barcode", "条码设置", "barcodeSetting", "system/barcodeSetting/index", "database");
// Menu outSet = new Menu(, "outSetting", "出库策略", "outSetting", "system/outSetting/index", "outSet"); // Menu outSet = new Menu(, "outSetting", "出库策略", "outSetting", "system/outSetting/index", "outSet");
......
...@@ -666,7 +666,7 @@ public class HttpHelper { ...@@ -666,7 +666,7 @@ public class HttpHelper {
List<NameValuePair> params = toNameValuePair(paramMap); List<NameValuePair> params = toNameValuePair(paramMap);
URI uri = new URIBuilder(url).setParameters(params).build(); URI uri = new URIBuilder(url).setParameters(params).build();
log.info("getParam请求,地址为:"+uri); //log.info("getParam请求,地址为:"+uri);
HttpGet httpGet = new HttpGet(uri); HttpGet httpGet = new HttpGet(uri);
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT).build(); RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECTION_TIMEOUT).build();
httpGet.setConfig(requestConfig); httpGet.setConfig(requestConfig);
......
...@@ -9,6 +9,7 @@ import com.neotel.smfcore.core.api.bean.CodeValidateParam; ...@@ -9,6 +9,7 @@ import com.neotel.smfcore.core.api.bean.CodeValidateParam;
import com.neotel.smfcore.core.device.util.DataCache; import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.inList.service.po.InList; import com.neotel.smfcore.core.inList.service.po.InList;
import com.neotel.smfcore.core.order.service.po.LiteOrder; import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog; import com.neotel.smfcore.core.system.service.po.DataLog;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings; import org.apache.logging.log4j.util.Strings;
...@@ -85,6 +86,12 @@ public class SmfApi { ...@@ -85,6 +86,12 @@ public class SmfApi {
@Value("${api.deviceStatusUrl:}") @Value("${api.deviceStatusUrl:}")
protected String deviceStatusUrl = ""; protected String deviceStatusUrl = "";
@Value("${api.mergePosUrl:}")
protected String mergePosUrl = "";
@Value("${api.splitPosUrl:}")
protected String splitPosUrl = "";
@PostConstruct @PostConstruct
public void init(){ public void init(){
apiName = dataCache.getConfigCache("api.name",apiName); apiName = dataCache.getConfigCache("api.name",apiName);
...@@ -100,6 +107,8 @@ public class SmfApi { ...@@ -100,6 +107,8 @@ public class SmfApi {
fetchOrderUrl = dataCache.getConfigCache("api.fetchOrderUrl",fetchOrderUrl); fetchOrderUrl = dataCache.getConfigCache("api.fetchOrderUrl",fetchOrderUrl);
loginCheckUrl=dataCache.getConfigCache("api.loginCheckUrl",loginCheckUrl); loginCheckUrl=dataCache.getConfigCache("api.loginCheckUrl",loginCheckUrl);
deviceStatusUrl=dataCache.getConfigCache("api.deviceStatusUrl",deviceStatusUrl); deviceStatusUrl=dataCache.getConfigCache("api.deviceStatusUrl",deviceStatusUrl);
mergePosUrl = dataCache.getConfigCache("api.mergePosUrl",mergePosUrl);
splitPosUrl = dataCache.getConfigCache("api.splitPosUrl",splitPosUrl);
} }
/** /**
...@@ -294,6 +303,37 @@ public class SmfApi { ...@@ -294,6 +303,37 @@ public class SmfApi {
} }
return true; return true;
} }
public void splitPos(String splitPosName) {
if(isUrlExist(splitPosUrl)){
threadPool.execute(new Runnable() {
@Override
public void run() {
for (ISmfApiListener apiListener : apiListenerList) {
if(apiListener.isForThisApi(apiName)){
apiListener.splitPos(splitPosUrl,splitPosName);
}
}
}
});
}
}
public void mergePos(StoragePos storagePos) {
if(isUrlExist(mergePosUrl)){
threadPool.execute(new Runnable() {
@Override
public void run() {
for (ISmfApiListener apiListener : apiListenerList) {
if(apiListener.isForThisApi(apiName)){
apiListener.mergePos(mergePosUrl,storagePos);
}
}
}
});
}
}
public String getApiName(){ public String getApiName(){
return apiName; return apiName;
} }
......
...@@ -20,6 +20,7 @@ import com.neotel.smfcore.core.inList.util.InListCache; ...@@ -20,6 +20,7 @@ import com.neotel.smfcore.core.inList.util.InListCache;
import com.neotel.smfcore.core.order.LiteOrderCache; import com.neotel.smfcore.core.order.LiteOrderCache;
import com.neotel.smfcore.core.order.service.manager.ILiteOrderManager; import com.neotel.smfcore.core.order.service.manager.ILiteOrderManager;
import com.neotel.smfcore.core.order.service.po.LiteOrder; import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog; import com.neotel.smfcore.core.system.service.po.DataLog;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings; import org.apache.logging.log4j.util.Strings;
...@@ -152,4 +153,14 @@ public abstract class BaseSmfApiListener implements ISmfApiListener { ...@@ -152,4 +153,14 @@ public abstract class BaseSmfApiListener implements ISmfApiListener {
public boolean deviceStatusChanges(String deviceStatusUrl, String[] cids,Integer[] status) { public boolean deviceStatusChanges(String deviceStatusUrl, String[] cids,Integer[] status) {
return true; return true;
} }
@Override
public void splitPos(String splitPosUrl, String splitPosName) {
}
@Override
public void mergePos(String mergePosUrl, StoragePos storagePos) {
}
} }
...@@ -6,6 +6,7 @@ import com.neotel.smfcore.core.barcode.service.po.Barcode; ...@@ -6,6 +6,7 @@ import com.neotel.smfcore.core.barcode.service.po.Barcode;
import com.neotel.smfcore.core.api.bean.CodeValidateParam; import com.neotel.smfcore.core.api.bean.CodeValidateParam;
import com.neotel.smfcore.core.inList.service.po.InList; import com.neotel.smfcore.core.inList.service.po.InList;
import com.neotel.smfcore.core.order.service.po.LiteOrder; import com.neotel.smfcore.core.order.service.po.LiteOrder;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog; import com.neotel.smfcore.core.system.service.po.DataLog;
public interface ISmfApiListener { public interface ISmfApiListener {
...@@ -80,4 +81,9 @@ public interface ISmfApiListener { ...@@ -80,4 +81,9 @@ public interface ISmfApiListener {
* @return * @return
*/ */
boolean deviceStatusChanges(String deviceStatusUrl, String[] cids,Integer[] status ); boolean deviceStatusChanges(String deviceStatusUrl, String[] cids,Integer[] status );
void splitPos(String splitPosUrl, String splitPosName);
void mergePos(String mergePosUrl, StoragePos storagePos);
} }
...@@ -18,6 +18,7 @@ import com.neotel.smfcore.core.dashboard.bean.dto.first.InOutDataDto; ...@@ -18,6 +18,7 @@ import com.neotel.smfcore.core.dashboard.bean.dto.first.InOutDataDto;
import com.neotel.smfcore.core.inout.service.manager.IInOutDataManager; import com.neotel.smfcore.core.inout.service.manager.IInOutDataManager;
import com.neotel.smfcore.core.inout.service.po.InOutData; import com.neotel.smfcore.core.inout.service.po.InOutData;
import com.neotel.smfcore.core.kanban.rest.bean.dto.BoxStatusDto; import com.neotel.smfcore.core.kanban.rest.bean.dto.BoxStatusDto;
import com.neotel.smfcore.core.kanban.rest.utils.NaturalOrderComparator;
import com.neotel.smfcore.core.language.util.MessageUtils; import com.neotel.smfcore.core.language.util.MessageUtils;
import com.neotel.smfcore.core.message.rest.bean.dto.MessageDto; import com.neotel.smfcore.core.message.rest.bean.dto.MessageDto;
import com.neotel.smfcore.core.message.rest.bean.mapstruct.MessageMapper; import com.neotel.smfcore.core.message.rest.bean.mapstruct.MessageMapper;
...@@ -105,7 +106,18 @@ public class SmdBoxMimoController { ...@@ -105,7 +106,18 @@ public class SmdBoxMimoController {
posCount += item.getTotalCount(); posCount += item.getTotalCount();
usePosCount += item.getUsedCount(); usePosCount += item.getUsedCount();
} }
InventoryBoxDto boxDto = new InventoryBoxDto(storage.getId(), storage.getName(), storage.getCid(), storage.getType(), storage.getUsageMap(), posCount, usePosCount);
Map<String, UsageItem> sortUsageMap = new LinkedHashMap<>();
Map<String, UsageItem> usageMap = storage.getUsageMap();
if (usageMap != null && !usageMap.isEmpty()) {
List<UsageItem> usageList = new ArrayList<>(usageMap.values());
usageList = usageList.stream().sorted(Comparator.comparing(UsageItem :: getW).thenComparing(UsageItem :: getH)).collect(Collectors.toList());
for (UsageItem usageItem : usageList) {
sortUsageMap.put(usageItem.getSizeStr(),usageItem);
}
}
InventoryBoxDto boxDto = new InventoryBoxDto(storage.getId(), storage.getName(), storage.getCid(), storage.getType(), sortUsageMap, posCount, usePosCount);
boxDtos.add(boxDto); boxDtos.add(boxDto);
} }
if (boxDtos.size() > 0) { if (boxDtos.size() > 0) {
......
...@@ -823,6 +823,7 @@ public class BaseDeviceHandler implements IDeviceHandler { ...@@ -823,6 +823,7 @@ public class BaseDeviceHandler implements IDeviceHandler {
//仓位状态 //仓位状态
barcodeDB.setCheckOutDate(new Date(), task.getOperator()); barcodeDB.setCheckOutDate(new Date(), task.getOperator());
barcodeDB.setPosName(""); barcodeDB.setPosName("");
barcodeDB.setOnlySingleOut(false);
barcodeManager.save(barcodeDB); barcodeManager.save(barcodeDB);
task.setBatchInfo(barcodeDB.getBatch()); task.setBatchInfo(barcodeDB.getBatch());
......
package com.neotel.smfcore.core.device.handler.impl;
import com.neotel.smfcore.core.storage.enums.DeviceType;
import org.springframework.stereotype.Service;
@Service
public class MimoG4BoxHandler extends MimoG3BoxHandler{
@Override
public DeviceType getDeviceType() {
return DeviceType.SMD_MIMO_G4;
}
}
package com.neotel.smfcore.core.device.handler.impl;
import cn.hutool.core.util.ObjectUtil;
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.common.utils.StringUtils;
import com.neotel.smfcore.core.api.bean.CodeValidateParam;
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.order.enums.ORDER_COLOR;
import com.neotel.smfcore.core.storage.service.po.Storage;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.bean.OrderSetting;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.security.TokenProvider;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Api(tags = "SHELF: NLP感应料架(浩博)")
@RestController
@Slf4j
public class NLPHBShelfHandler extends BaseDeviceHandler {
@Autowired
private TokenProvider tokenProvider;
@Autowired
private DataCache dataCache;
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
@PostConstruct
private void init() {
scheduledThreadPool.scheduleWithFixedDelay(() -> {
OrderSetting orderSetting = dataCache.getOrderSetting();
boolean openZhiYin = orderSetting.getShelfLightType().equals(1);
Map<String, List<DataLog>> outMap = new HashMap<>();
for (Storage storage : dataCache.getAllStorage().values()) {
if (storage.isNLPHBShelf()) {
Collection<DataLog> queueTasks = taskService.getQueueTasks(storage.getCid());
if (queueTasks != null && !queueTasks.isEmpty()) {
for (DataLog queueTask : queueTasks) {
if (!openZhiYin || StringUtils.isBlank(queueTask.getSourceId())) {
if (queueTask.isWait() && queueTask.isCheckOutTask()) {
String rgb = queueTask.getLightColor();
ORDER_COLOR color = ORDER_COLOR.fromRgb(rgb);
if (color == null) {
color = ORDER_COLOR.BLUE;
}
queueTask.setLightColor(color.getRgb());
queueTask.setStatus(OP_STATUS.EXECUTING.name());
taskService.updateQueueTask(queueTask);
log.info("库位[" + queueTask.getPosName() + "][" + queueTask.getType() + "]+亮灯:" + color.name());
}
} else {
if (queueTask.isWait() || queueTask.isExecuting()) {
List<DataLog> dataLogList = outMap.get(queueTask.getSourceId());
if (dataLogList == null) {
dataLogList = new ArrayList<>();
}
dataLogList.add(queueTask);
outMap.put(queueTask.getSourceId(), dataLogList);
}
}
}
}
List<DataLog> dataLogs = getLightGuideTask(outMap, storage.getCid());
for (DataLog task : dataLogs) {
if (task.isWait()) {
if (StringUtils.isBlank(task.getLightColor())) {
task.setLightColor(ORDER_COLOR.BLUE.getRgb());
}
task.setStatus(OP_STATUS.EXECUTING.name());
taskService.updateQueueTask(task);
log.info("库位[" + task.getPosName() + "][" + task.getType() + "]+亮灯:" + task.getLightColor());
}
}
}
}
}, 30, 1, TimeUnit.SECONDS);
}
@ApiOperation("扫码入库")
@PostMapping("/api/nlpHBShelf/codeIn")
@PreAuthorize("@el.check('nlpHBShelf: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 sourceId = mapValues.get("sourceId");
String token = tokenProvider.getToken(request);
String amountStr = mapValues.get("amount");
if (ObjectUtils.isEmpty(code)) {
throw new ValidateException("smfcore.valueCanotNull", "{0}不能为空", new String[]{"code"});
}
code = replaceSpecial(code);
if (groupId == null && storageId == null) {
throw new ValidateException("smfcore.valueCanotNull", "{0}不能为空", new String[]{"group"});
}
if (groupId == null && storageId != null) {
Storage storage = dataCache.getStorageById(storageId);
if (storage != null) {
groupId = storage.getGroupId();
}
}
if (groupId != null && groupId.equals("-1")) {
groupId = "";
}
// WebSocketServer.sendMsg("", new SocketMsg("{0}未找到库位:{1}"+code, MsgType.INFO,"smfclient.nlp.cannotFindPos",new String[]{"消息测试","库位号"}));
String loginUser = SecurityUtils.getLoginUsername();
Collection<DataLog> queueTasks = taskService.getQueueTasks();
for (DataLog queueTask : queueTasks) {
if (queueTask.isPutInTask() && (queueTask.isWait() || queueTask.isExecuting())) {
if (!Strings.isNullOrEmpty(groupId) && queueTask.getGroupId().equals(groupId)) {
log.info("codeIn [" + code + "][" + groupId + "]入库时取消条码[" + queueTask.getBarcode() + "]的未完成入库任务");
//return ResultBean.newErrorResult(-1,"smfcore.unfinished","the task of [{0}] is unfinished",new String[]{queueTask.getBarcode()});
taskService.cancelTask(queueTask.getId());
}
if (!Strings.isNullOrEmpty(storageId) && queueTask.getStorageId().equals(storageId)) {
log.info("codeIn [" + code + "][" + storageId + "]入库时取消料架[" + queueTask.getStorageId() + "]的未完成入库任务");
//return ResultBean.newErrorResult(-1,"smfcore.unfinished","the task of [{0}] is unfinished",new String[]{queueTask.getStorageName()});
taskService.cancelTask(queueTask.getId());
}
}
}
try {
CodeValidateParam params = new CodeValidateParam(loginUser, groupId, storageId, code, token);
Barcode barcodeSave = smfApi.canPutInBeforeResolve(params);
if (barcodeSave == null) {
barcodeSave = codeResolve.resolveOneValideBarcode("=1x1=" + code);
}
//从API验证
Barcode verResult = smfApi.canPutInAfterResolve(params, barcodeSave);
if (verResult != null) {
barcodeSave = verResult;
}
return putIn(loginUser, groupId, storageId, barcodeSave, token, amountStr);
} catch (ValidateException ve) {
return ResultBean.newErrorResult(-1, ve.getMsgKey(), ve.getDefaultMsg(), ve.getMsgParam());
}
}
protected ResultBean putIn(String loginUser, String groupId, String storageId, Barcode barcode, String token, String amountStr) {
if (barcode == null || barcode.getBarcode() == null) {
return ResultBean.newErrorResult(1, "smfcore.error.barcode.noValidCode", "无效的条码");
}
StoragePos pos = storagePosManager.getByBarcode(barcode.getBarcode());
if (pos != null) {
throw new ValidateException("smfcore.materialBox.inPos", "物料已在库位{0}中", new String[]{pos.getPosName()});
}
if (barcode.getAmount() <= 0) {
throw new ValidateException("smfcore.error.barcode.wrongQty", "条码[{0}]对应的数量<=0为: {1}", new String[]{barcode.getBarcode(), barcode.getAmount() + ""});
}
Date expireDate = barcode.getExpireDate();
if (expireDate != null) {
if (System.currentTimeMillis() > expireDate.getTime()) {
throw new ValidateException("smfcore.error.barcode.expired", "物料已过期,无法入库.");
}
}
if (dataCache.shelfConfirmPutIn()) {
if (barcode.isPutIn()) {
if (amountStr == null) {
ResultBean resultBean = ResultBean.newOkResult(barcode);
resultBean.setCode(99);
return resultBean;
} else {
int amount = 0;
try {
amount = Integer.parseInt(amountStr);
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (amount <= 0) {
throw new ValidateException("smfcore.materialBox.qtyError", "请输入正确的数量");
}
barcode.setAmount(amount);
}
}
}
String pn = barcode.getPartNumber();
String reelId = barcode.getBarcode();
String num = barcode.getAmount() + "";
Integer qty = barcode.getAmount();
String msl = barcode.getMsl();
try {
//需要模拟一个库位
DataLog dataLog = new DataLog();
dataLog.setBarcode(reelId);
dataLog.setPartNumber(pn);
dataLog.setType(OP.PUT_IN);
barcode.setPutInTime(System.currentTimeMillis());
barcode.updateSluggishTime(dataCache.getPNsluggishDay(barcode.getPartNumber()));
barcodeManager.saveBarcode(barcode);
dataLog.setNum(barcode.getAmount());
dataLog.setStatus(OP_STATUS.WAIT.name());
dataLog.setGroupId(groupId);
dataLog.setStorageId(storageId);
dataLog.setMemo(barcode.getMemo());
dataLog.setOperator(loginUser);
if (ObjectUtil.isNotEmpty(storageId)) {
Storage storage = dataCache.getStorageById(storageId);
if (storage != null) {
dataLog.setCid(storage.getCid());
dataLog.setStorageName(storage.getName());
}
}
try {
taskService.addTaskToExecute(dataLog);
} catch (Exception e) {
return ResultBean.newErrorResult(-1, "smfclient.nlp.error", "入库失败:" + e.getMessage(), new String[]{e.getMessage()});
}
} catch (ValidateException e) {
log.error(e.toString());
return ResultBean.newErrorResult(1, e.getMsgKey(), e.getDefaultMsg());
}
return ResultBean.newOkResult("");
}
}
...@@ -242,32 +242,32 @@ public class NLPShelfHandler extends BaseDeviceHandler { ...@@ -242,32 +242,32 @@ public class NLPShelfHandler extends BaseDeviceHandler {
//亮灯 //亮灯
Collection<DataLog> queueTasks = taskService.getQueueTasks(statusBean.getCid()); Collection<DataLog> queueTasks = taskService.getQueueTasks(statusBean.getCid());
for (DataLog queueTask : queueTasks) { for (DataLog queueTask : queueTasks) {
if (queueTask.isWait() && queueTask.isCheckOutTask()) { if (!openZhiYin || StringUtils.isBlank(queueTask.getSourceId())) {
queueTask.setStatus(OP_STATUS.EXECUTING.name()); if (queueTask.isWait()) {
taskService.updateQueueTask(queueTask);
String rgb = queueTask.getLightColor(); String rgb = queueTask.getLightColor();
ORDER_COLOR color = ORDER_COLOR.fromRgb(rgb); ORDER_COLOR color = ORDER_COLOR.fromRgb(rgb);
if (color == null) { if (color == null) {
if (openZhiYin && ObjectUtil.isNotEmpty(queueTask.getSourceId())) { if (queueTask.isPutInTask()) {
color = ORDER_COLOR.FORESTGREEN;
} else {
color = ORDER_COLOR.BLUE;
}
}
queueTask.setStatus(OP_STATUS.EXECUTING.name());
taskService.updateQueueTask(queueTask);
statusBean.addData("open", queueTask.getPosName() + "=" + color.name());
log.info("库位[" + queueTask.getPosName() + "][" + queueTask.getType() + "]+亮灯:" + color.name());
}
} else {
if (queueTask.isWait() || queueTask.isExecuting()) {
List<DataLog> dataLogList = outMap.get(queueTask.getSourceId()); List<DataLog> dataLogList = outMap.get(queueTask.getSourceId());
if (dataLogList == null) { if (dataLogList == null) {
dataLogList = new ArrayList<>(); dataLogList = new ArrayList<>();
} }
dataLogList.add(queueTask); dataLogList.add(queueTask);
outMap.put(queueTask.getSourceId(), dataLogList); outMap.put(queueTask.getSourceId(), dataLogList);
} else {
if (StringUtils.isNotBlank(rgb)){
log.info(queueTask.getPosName() + "库位亮灯:" + "#" + rgb);
statusBean.addData("open", queueTask.getPosName() + "=#" + rgb);
} else {
color = ORDER_COLOR.BLUE;
}
}
}
if (color != null){
statusBean.addData("open", queueTask.getPosName() + "=" + color.name());
log.info("库位[" + queueTask.getPosName() + "]+亮灯:" + color.name());
} }
} }
} }
......
...@@ -820,7 +820,7 @@ public class DataCache { ...@@ -820,7 +820,7 @@ public class DataCache {
public List<String> getAvailableStorageIds() { public List<String> getAvailableStorageIds() {
List<String> availableStorageIds = new ArrayList<>(); List<String> availableStorageIds = new ArrayList<>();
for (Storage storage : getAllStorage().values()) { for (Storage storage : getAllStorage().values()) {
if (!storage.isVirtual()) { if (!storage.isVirtual() && !storage.isNLPHBShelf()) {
StatusBean bean = DevicesStatusUtil.getStatusBean(storage.getCid()); StatusBean bean = DevicesStatusUtil.getStatusBean(storage.getCid());
if (bean == null || bean.timeOut() || !bean.isAvailable()) { if (bean == null || bean.timeOut() || !bean.isAvailable()) {
continue; continue;
...@@ -832,6 +832,7 @@ public class DataCache { ...@@ -832,6 +832,7 @@ public class DataCache {
} }
return availableStorageIds; return availableStorageIds;
} }
public List<String> getAvailableStorageIds(List<String> cidList) { public List<String> getAvailableStorageIds(List<String> cidList) {
if (cidList == null || cidList.size() <= 0) { if (cidList == null || cidList.size() <= 0) {
return getAvailableStorageIds(); return getAvailableStorageIds();
...@@ -841,7 +842,7 @@ public class DataCache { ...@@ -841,7 +842,7 @@ public class DataCache {
if (!cidList.contains(storage.getCid())) { if (!cidList.contains(storage.getCid())) {
continue; continue;
} }
if (!storage.isVirtual()) { if (!storage.isVirtual() && !storage.isNLPHBShelf()) {
StatusBean bean = DevicesStatusUtil.getStatusBean(storage.getCid()); StatusBean bean = DevicesStatusUtil.getStatusBean(storage.getCid());
if (bean == null || bean.timeOut() || !bean.isAvailable()) { if (bean == null || bean.timeOut() || !bean.isAvailable()) {
continue; continue;
...@@ -853,6 +854,7 @@ public class DataCache { ...@@ -853,6 +854,7 @@ public class DataCache {
} }
return availableStorageIds; return availableStorageIds;
} }
public List<String> getAvailableStorageIds(DeviceType deviceType) { public List<String> getAvailableStorageIds(DeviceType deviceType) {
List<String> availableStorageIds = new ArrayList<>(); List<String> availableStorageIds = new ArrayList<>();
for (Storage storage : getAllStorage().values()) { for (Storage storage : getAllStorage().values()) {
......
...@@ -455,7 +455,7 @@ public class BoxKanbanController { ...@@ -455,7 +455,7 @@ public class BoxKanbanController {
boxDto.setOnLine(false); boxDto.setOnLine(false);
//如果是虚拟仓,默认在线 //如果是虚拟仓,默认在线
if (storage.isVirtual()) { if (storage.isVirtual() || storage.isNLPHBShelf()) {
boxDto.setOnLine(true); boxDto.setOnLine(true);
boxDto.setStatus(1); boxDto.setStatus(1);
} }
......
...@@ -33,16 +33,16 @@ public enum ORDER_COLOR { ...@@ -33,16 +33,16 @@ public enum ORDER_COLOR {
//MAGENTA("FF00FF"), //MAGENTA("FF00FF"),
CYAN("00FFFF"), CYAN("00FFFF"),
FIREBRICK("B22222"), FIREBRICK("B22222"),
PURPLE("A020F0"), //PURPLE("A020F0"),
//SKYBLUE("6CA6CD"), //SKYBLUE("6CA6CD"),
//PINK("FF1493"), //PINK("FF1493"),
FORESTGREEN("228B22"), FORESTGREEN("228B22"),
//LIGHTBLUE("8470FF"), //LIGHTBLUE("8470FF"),
//INDIANRED("8B3A3A"), //INDIANRED("8B3A3A"),
DARKGREEN("556B2F"), //DARKGREEN("556B2F"),
RED("FF0000"), RED("FF0000"),
YELLOW("FFFF00"), YELLOW("FFFF00"),
ORANGE("FFA500"), //ORANGE("FFA500"),
WHITE("FFFFFF") WHITE("FFFFFF")
; ;
......
...@@ -161,9 +161,21 @@ public enum DeviceType { ...@@ -161,9 +161,21 @@ public enum DeviceType {
SMD_ONE("storage.type.smdOne"), SMD_ONE("storage.type.smdOne"),
/** /**
* 尾料柜 * 27 尾料柜
*/ */
TAILING_MATERIAL("storage.type.tailingMaterial"), TAILING_MATERIAL("storage.type.tailingMaterial"),
/**
* 28 SMD_MIMO_G4
*/
SMD_MIMO_G4("storage.type.smdMimoG4"),
/**
* 29 感应料架NLP(浩博)
*/
NLP_HB("storage.type.nlpHb"),
; ;
private String key; private String key;
...@@ -185,6 +197,6 @@ public enum DeviceType { ...@@ -185,6 +197,6 @@ public enum DeviceType {
} }
public static List<DeviceType> availableTypeList(){ public static List<DeviceType> availableTypeList(){
return Lists.newArrayList(AUTO,LINE,BATCH,SOLDERPASTE,VERTICALBOX,SMD_XL,SMD_DUO,SMD_XLC,SMD_XLR,SMD_ONE,VIRTUAL,NL,NLP,NLM,NLL,NLS,NLSM,SMDBOX_THIRD,SMD_MIMO_G2,SMD_MIMO_G3,TAILING_MATERIAL); return Lists.newArrayList(AUTO,LINE,BATCH,SOLDERPASTE,VERTICALBOX,SMD_XL,SMD_DUO,SMD_XLC,SMD_XLR,SMD_ONE,VIRTUAL,NL,NLP,NLP_HB,NLM,NLL,NLS,NLSM,SMDBOX_THIRD,SMD_MIMO_G2,SMD_MIMO_G3,SMD_MIMO_G4,TAILING_MATERIAL);
} }
} }
package com.neotel.smfcore.core.storage.rest;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.neotel.smfcore.common.bean.PageData;
import com.neotel.smfcore.common.bean.ResultBean;
import com.neotel.smfcore.common.utils.QueryHelp;
import com.neotel.smfcore.common.utils.ReelLockPosUtil;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.api.SmfApi;
import com.neotel.smfcore.core.barcode.enums.BARCODE_SOURCE;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.storage.rest.dto.StoragePosDto;
import com.neotel.smfcore.core.storage.rest.mapstruct.StoragePosMapper;
import com.neotel.smfcore.core.storage.rest.query.StoragePosQueryCriteria;
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.security.annotation.AnonymousAccess;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
import java.util.stream.Collectors;
@Api(tags = "库位合并")
@Slf4j
@RestController
@RequestMapping("/storagePosMerge")
public class StoragePosMergeController {
@Autowired
private IStoragePosManager storagePosManager;
@Autowired
private DataCache dataCache;
@Autowired
private StoragePosMapper storagePosMapper;
@Autowired
private SmfApi smfApi;
@ApiOperation("查询所有库位")
@GetMapping("findAllPos")
public ResultBean findAllPos(StoragePosQueryCriteria criteria) {
if (criteria.getStorageIdList() != null && criteria.getStorageIdList().contains("0")) {
criteria.setStorageIdList(null);
}
String blurry = criteria.getBlurry();
if (!Strings.isNullOrEmpty(blurry)) {
blurry = blurry.substring(blurry.indexOf("-") + 1);
criteria.setBlurry(blurry);
}
Query query = QueryHelp.getQuery(criteria);
List<String> storageIdList = criteria.getStorageIdList();
if (storageIdList == null || storageIdList.isEmpty()) {
List<String> notFindStorageId = new ArrayList<>();
Collection<Storage> storages = dataCache.getAllStorage().values();
String pageType = criteria.getType();
for (Storage storage : storages) {
if (BARCODE_SOURCE.VIRTUAL.equals(pageType)) {
if (!storage.isVirtual()) {
notFindStorageId.add(storage.getId());
}
} else {
if (storage.isVirtual()) {
notFindStorageId.add(storage.getId());
}
}
}
if (!notFindStorageId.isEmpty()) {
query.addCriteria(Criteria.where("storageId").nin(notFindStorageId));
}
}
List<StoragePos> storagePosList = storagePosManager.findByQuery(query);
List<StoragePosDto> storagePosDtoList = storagePosMapper.toDto(storagePosList);
Set<String> allLockPosIds = ReelLockPosUtil.getAllLockPosIds();
for (int i = 0; i < storagePosDtoList.size(); i++) {
Storage storage = dataCache.getStorageById(storagePosDtoList.get(i).getStorageId());
if (storage != null) {
storagePosDtoList.get(i).setStorageName(storage.getName());
if (allLockPosIds.contains(storagePosDtoList.get(i).getId())) {
storagePosDtoList.get(i).setLock(true);
}
}
}
return ResultBean.newOkResult(storagePosDtoList);
}
@ApiOperation("库位合并")
@RequestMapping("mergePos")
public ResultBean mergePos(@RequestBody Map<String,String> paramMap) {
String mergePosListStr = paramMap.get("mergePosList");
String mergeNumStr = paramMap.get("mergeNum");
String storageId = paramMap.get("storageId");
if (StringUtils.isEmpty(mergePosListStr)){
return ResultBean.newErrorResult(-1,"smfcore.pos.merge.notNull","库位不能为空");
}
List<String> mergePosList = JSONObject.parseArray(mergePosListStr,String.class);
if (mergePosList == null){
mergePosList = new ArrayList<>();
}
int mergeNum = Integer.parseInt(mergeNumStr);
//判断库位与传入的值,必须一致
if (mergePosList.size() != mergeNum){
return ResultBean.newErrorResult(-1,"smfcore.pos.merge.numError","请选择[{0}]个要合并的库位",new String[]{mergeNumStr});
}
List<StoragePos> storagePosList = storagePosManager.findPosList(storageId, mergePosList);
//判断是否有物料
for (StoragePos pos : storagePosList) {
if (pos.getBarcode() != null){
return ResultBean.newErrorResult(-1,"smfcore.pos.merge.hasMaterial","库位[{0}]有物料,不能合并",new String[]{pos.getPosName()});
}
List<String> mergePosNameList = pos.getMergePosList();
if (mergePosNameList != null && !mergePosNameList.isEmpty()){
return ResultBean.newErrorResult(-1,"smfcore.pos.merge.hasMergePos","库位[{0}]已合并,不能继续合并",new String[]{pos.getPosName()});
}
}
//判断是否为相邻的库位
storagePosList = storagePosList.stream().sorted(Comparator.comparing(StoragePos::getLayer).thenComparing(StoragePos::getColumn)).collect(Collectors.toList());
boolean isSameLayerAndAdjacent = isAllSameLayerAndAdjacent(storagePosList);
if (!isSameLayerAndAdjacent){
return ResultBean.newErrorResult(-1,"smfcore.pos.merge.notAdjacent","请选择相邻的库位合并");
}
//设置合并库位信息
StoragePos storagePos = storagePosList.get(0);
List<String> mergePosNameList = new ArrayList<>();
for (StoragePos pos : storagePosList) {
mergePosNameList.add(pos.getPosName());
}
storagePos.setMergePosList(mergePosNameList);
storagePosManager.saveMergePos(storagePos);
smfApi.mergePos(storagePos);
return ResultBean.newOkResult("");
}
@ApiOperation("库位拆分")
@RequestMapping("splitPos")
public ResultBean splitPos(@RequestBody Map<String,String> paramMap) {
String splitPosName = paramMap.get("splitPosName");
if (StringUtils.isEmpty(splitPosName)){
return ResultBean.newErrorResult(-1,"smfcore.pos.split.notNull","库位不能为空");
}
StoragePos pos = storagePosManager.getByPosName(splitPosName);
if (pos == null){
return ResultBean.newErrorResult(-1,"smfcore.pos.split.notNull","库位不能为空");
}
List<String> mergePosList = pos.getMergePosList();
if (mergePosList == null || mergePosList.isEmpty()){
return ResultBean.newErrorResult(-1,"smfcore.pos.split.notMergePos","库位[{0}]未合并,不能拆分",new String[]{splitPosName});
}
if (pos.getBarcode() != null){
return ResultBean.newErrorResult(-1,"smfcore.pos.split.hasMaterial","库位[{0}]有物料,不能拆分",new String[]{splitPosName});
}
pos.setMergePosList(null);
storagePosManager.saveMergePos(pos);
smfApi.splitPos(splitPosName);
return ResultBean.newOkResult("");
}
/**
* 核心方法:传入库位列表,判断所有连续库位是否「同层 + 左右相邻」(层/列均为String类型)
* @param storagePosList 库位列表
* @return true=所有连续库位满足条件;false=不满足/列表无效
*/
public static boolean isAllSameLayerAndAdjacent(List<StoragePos> storagePosList) {
// 1. 空列表/单库位直接返回false
if (storagePosList == null || storagePosList.size() < 2) {
log.warn("库位列表为空或仅1个库位,无需判断相邻");
return false;
}
// 3. 遍历判断每一对连续库位
for (int i = 0; i < storagePosList.size() - 1; i++) {
StoragePos curr = storagePosList.get(i);
StoragePos next = storagePosList.get(i + 1);
// 转数字(空/非数字直接返回false)
Integer currLayer = strToInt(curr.getLayer());
Integer nextLayer = strToInt(next.getLayer());
Integer currCol = strToInt(curr.getColumn());
Integer nextCol = strToInt(next.getColumn());
// 4. 核心判断:层/列非空 + 同层 + 列差=1
if (currLayer == null || nextLayer == null || currCol == null || nextCol == null) {
log.warn("库位{}和{}的层/列非有效数字,不满足相邻条件", curr.getPosName(), next.getPosName());
return false;
}
if (!currLayer.equals(nextLayer)) {
log.warn("库位{}和{}不同层({} vs {}),不满足相邻条件", curr.getPosName(), next.getPosName(), curr.getLayer(), next.getLayer());
return false;
}
if (Math.abs(currCol - nextCol) != 1) {
log.warn("库位{}和{}非左右相邻(列:{} vs {}),不满足条件", curr.getPosName(), next.getPosName(), curr.getColumn(), next.getColumn());
return false;
}
}
// 所有库位满足条件
return true;
}
/**
* 内部工具:String转Integer(空/非数字返回null,避免异常)
*/
private static Integer strToInt(String str) {
if (StringUtils.isEmpty(str)) {
return null;
}
try {
return Integer.parseInt(str.trim());
} catch (NumberFormatException e) {
log.warn("字符串[{}]无法转为数字", str);
return null;
}
}
}
...@@ -107,4 +107,5 @@ public interface IStoragePosManager extends IBaseManager<StoragePos> { ...@@ -107,4 +107,5 @@ public interface IStoragePosManager extends IBaseManager<StoragePos> {
StoragePos getSpEmptyPosByStorage(Storage storage, Barcode barcode, Collection<String> excludePosIds,String endStr) throws ValidateException; StoragePos getSpEmptyPosByStorage(Storage storage, Barcode barcode, Collection<String> excludePosIds,String endStr) throws ValidateException;
void saveMergePos(StoragePos storagePos);
} }
...@@ -1176,4 +1176,9 @@ public class StoragePosManagerImpl implements IStoragePosManager { ...@@ -1176,4 +1176,9 @@ public class StoragePosManagerImpl implements IStoragePosManager {
StoragePos pos = storagePosDao.findOne(query); StoragePos pos = storagePosDao.findOne(query);
return pos; return pos;
} }
@Override
public void saveMergePos(StoragePos storagePos) {
storagePosDao.save(storagePos);
}
} }
...@@ -207,6 +207,10 @@ public class Storage extends BasePo implements Serializable { ...@@ -207,6 +207,10 @@ public class Storage extends BasePo implements Serializable {
return DeviceType.SMD_XLC.name().equals(type); return DeviceType.SMD_XLC.name().equals(type);
} }
public boolean isNLPHBShelf() {
return DeviceType.NLP_HB.name().equals(type);
}
public boolean isType(DeviceType[] types){ public boolean isType(DeviceType[] types){
for (DeviceType str : for (DeviceType str :
types) { types) {
......
...@@ -157,7 +157,7 @@ public class SettingsController { ...@@ -157,7 +157,7 @@ public class SettingsController {
List<Storage> storages = new ArrayList<>(dataCache.getAllStorage().values()); List<Storage> storages = new ArrayList<>(dataCache.getAllStorage().values());
for (Storage storage : storages for (Storage storage : storages
) { ) {
if (storage.isType(new DeviceType[]{DeviceType.NLP,DeviceType.NL,DeviceType.NLS})) { if (storage.isType(new DeviceType[]{DeviceType.NLP,DeviceType.NL,DeviceType.NLS,DeviceType.NLP_HB})) {
orderSetting.setShowLightType(true); orderSetting.setShowLightType(true);
break; break;
} }
......
package com.neotel.smfcore.custom.haobo;
import com.neotel.smfcore.core.api.SmfApi;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.storage.service.po.Storage;
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.custom.haobo.bean.PosDiff;
import com.neotel.smfcore.custom.haobo.bean.PosTotalDiffResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class HaoBoNlpPosHandler {
@Autowired
private SmfApi smfApi;
@Autowired
private HaoboApi haoboApi;
@Autowired
private DataCache dataCache;
@Autowired
private TaskService taskService;
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
@PostConstruct
private void init() {
scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
String apiName = smfApi.getApiName();
if (haoboApi.isForThisApi(apiName)) {
handlePos();
}
}
}, 30, 5, TimeUnit.SECONDS);
}
private void handlePos() {
for (Storage storage : dataCache.getAllStorage().values()) {
String cid = storage.getCid(); // 提取CID,便于日志定位
if (storage.isNLPHBShelf()) {
// ========== 1. 获取旧数据(空值兜底,避免索引越界) ==========
List<List<String>> oldDeviceData = DevicesStatusUtil.getDeviceData(cid);
// ========== 2. 计算新的差异数据 ==========
List<String> inventoryPosName = haoboApi.getInventoryPosName(cid);
List<String> usedPosNameList = dataCache.getUsedPosNameList(cid);
PosDiff posDiff = calculatePosDiff(inventoryPosName, usedPosNameList);
List<String> newInventoryOnly = posDiff.getInventoryOnly() == null ? new ArrayList<>() : posDiff.getInventoryOnly();
List<String> newUsedOnly = posDiff.getUsedOnly() == null ? new ArrayList<>() : posDiff.getUsedOnly();
List<List<String>> newData = new ArrayList<>();
newData.add(newInventoryOnly);
newData.add(newUsedOnly);
//3.开始新增差异信息,亮灯或者灭灯
PosTotalDiffResult posTotalDiffResult = calculateAndPrintPosDiff(oldDeviceData, newData);
List<String> totalAddList = posTotalDiffResult.getTotalAddList();
List<String> totalReduceList = posTotalDiffResult.getTotalReduceList();
haoboApi.lightUpSomeLampBeads(totalAddList, totalReduceList);
DevicesStatusUtil.updateDeviceData(cid, newData);
}
}
}
/**
* 计算并打印新旧货位数据的差异,同时返回合并后的总新增/总减少列表
*
* @param oldDeviceData 旧设备数据(二维列表:[库存独有列表, 已用独有列表])
* @param newData 新计算的差异数据(二维列表:[库存独有列表, 已用独有列表])
* @return PosTotalDiffResult 合并后的总新增、总减少货位列表
*/
private PosTotalDiffResult calculateAndPrintPosDiff(List<List<String>> oldDeviceData, List<List<String>> newData) {
// ========== 1. 解析旧数据(空值兜底) ==========
List<String> oldInventoryOnly = new ArrayList<>();
List<String> oldUsedOnly = new ArrayList<>();
if (oldDeviceData != null && !oldDeviceData.isEmpty()) {
oldInventoryOnly = oldDeviceData.size() >= 1 ? oldDeviceData.get(0) : new ArrayList<>();
oldUsedOnly = oldDeviceData.size() >= 2 ? oldDeviceData.get(1) : new ArrayList<>();
}
oldInventoryOnly = oldInventoryOnly == null ? new ArrayList<>() : oldInventoryOnly;
oldUsedOnly = oldUsedOnly == null ? new ArrayList<>() : oldUsedOnly;
// ========== 2. 解析新数据(空值兜底) ==========
List<String> newInventoryOnly = new ArrayList<>();
List<String> newUsedOnly = new ArrayList<>();
if (newData != null && !newData.isEmpty()) {
newInventoryOnly = newData.size() >= 1 ? newData.get(0) : new ArrayList<>();
newUsedOnly = newData.size() >= 2 ? newData.get(1) : new ArrayList<>();
}
newInventoryOnly = newInventoryOnly == null ? new ArrayList<>() : newInventoryOnly;
newUsedOnly = newUsedOnly == null ? new ArrayList<>() : newUsedOnly;
// ========== 3. 计算细分维度差异 ==========
Set<String> oldInventorySet = new HashSet<>(oldInventoryOnly);
Set<String> newInventorySet = new HashSet<>(newInventoryOnly);
Set<String> inventoryAdd = new HashSet<>(newInventorySet);
inventoryAdd.removeAll(oldInventorySet);
Set<String> inventoryReduce = new HashSet<>(oldInventorySet);
inventoryReduce.removeAll(newInventorySet);
Set<String> oldUsedSet = new HashSet<>(oldUsedOnly);
Set<String> newUsedSet = new HashSet<>(newUsedOnly);
Set<String> usedAdd = new HashSet<>(newUsedSet);
usedAdd.removeAll(oldUsedSet);
Set<String> usedReduce = new HashSet<>(oldUsedSet);
usedReduce.removeAll(newUsedSet);
// ========== 4. 合并总差异(去重)并转List ==========
Set<String> totalAddSet = new HashSet<>();
totalAddSet.addAll(inventoryAdd);
totalAddSet.addAll(usedAdd);
List<String> totalAddList = new ArrayList<>(totalAddSet); // 转List更易用
Set<String> totalReduceSet = new HashSet<>();
totalReduceSet.addAll(inventoryReduce);
totalReduceSet.addAll(usedReduce);
List<String> totalReduceList = new ArrayList<>(totalReduceSet); // 转List更易用
// ========== 6. 返回合并后的差异结果 ==========
return new PosTotalDiffResult(totalAddList, totalReduceList);
}
/**
* 计算两个货位名称列表的双向差集(纯String类型,无DataItem依赖)
*
* @param inventoryPosNameList 库存货位名称列表
* @param usedPosNameList 已用货位名称列表
* @return PosDiff inventoryOnly=库存有但已用无的名称列表 | usedOnly=已用有但库存无的名称列表
*/
private PosDiff calculatePosDiff(List<String> inventoryPosNameList, List<String> usedPosNameList) {
// 1. 空值兜底:避免NPE
List<String> safeInventoryNames = inventoryPosNameList == null ? new ArrayList<>() : inventoryPosNameList;
List<String> safeUsedNames = usedPosNameList == null ? new ArrayList<>() : usedPosNameList;
// 2. 转Set去重+提高差集计算效率(纯String操作)
Set<String> inventoryPosSet = new HashSet<>(safeInventoryNames);
Set<String> usedPosSet = new HashSet<>(safeUsedNames);
// 3. 计算:库存有但已用无的货位名称列表
Set<String> inventoryOnlySet = new HashSet<>(inventoryPosSet);
inventoryOnlySet.removeAll(usedPosSet);
List<String> inventoryOnly = new ArrayList<>(inventoryOnlySet);
// 4. 计算:已用有但库存无的货位名称列表
Set<String> usedOnlySet = new HashSet<>(usedPosSet);
usedOnlySet.removeAll(inventoryPosSet);
List<String> usedOnly = new ArrayList<>(usedOnlySet);
// 5. 返回纯String类型的差集结果
return new PosDiff(inventoryOnly, usedOnly);
}
}
package com.neotel.smfcore.custom.haobo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.neotel.smfcore.common.exception.ApiException;
import com.neotel.smfcore.common.utils.DateUtil;
import com.neotel.smfcore.common.utils.HttpHelper;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.api.listener.BaseSmfApiListener;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.order.enums.ORDER_COLOR;
import com.neotel.smfcore.core.storage.service.po.StoragePos;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.custom.haobo.bean.response.GetInventoryResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
@Slf4j
@Service
public class HaoboApi extends BaseSmfApiListener {
@Value("${api.getInventoryUrl:}")
private String getInventoryUrl = "";
@Value("${api.lightUpLabelUrl:}")
private String lightUpLabelUrl ="";
@Value("${api.lightUpSomeLampBeadsUrl:}")
private String lightUpSomeLampBeadsUrl ="";
@Value("${api.controlIoBoardUrl:}")
private String controlIoBoardUrl ="";
@Autowired
private DataCache dataCache;
@PostConstruct
private void init() {
getInventoryUrl = dataCache.getConfigCache("api.haobo.getInventory", getInventoryUrl);
lightUpLabelUrl = dataCache.getConfigCache("api.haobo.lightUpLabel", lightUpLabelUrl);
lightUpSomeLampBeadsUrl = dataCache.getConfigCache("api.haobo.lightUpSomeLampBeads", lightUpSomeLampBeadsUrl);
controlIoBoardUrl = dataCache.getConfigCache("api.haobo.controlIoBoard", controlIoBoardUrl);
}
@Override
public boolean isForThisApi(String apiName) {
return "haobo".equalsIgnoreCase(apiName);
}
@Override
public void inTaskStatusChange(String inNotifyUrl, DataLog task) {
if (task.isPutInTask() && task.isWait()){
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("labelId",task.getBarcode());
paramMap.put("shelfCode",task.getCid());
paramMap.put("operationTime", DateUtil.toDateString(task.getCreateDate(),"yyyy/MM/dd HH:mm:ss"));
paramMap.put("detailsJson","");
log.info(task.getBarcode()+":入库通知,请求浩博料架,地址为:"+inNotifyUrl+",参数为:"+ JSON.toJSONString(paramMap));
try {
String result = HttpHelper.postJson(inNotifyUrl, paramMap);
log.info(task.getBarcode()+":入库通知,浩博料架返回结果为:"+result);
} catch (ApiException e) {
log.info(task.getBarcode()+":入库通知,浩博料架返回异常,异常信息为:"+e.getMessage());
}
}
}
@Override
public void outTaskStatusChange(String outNotifyUrl, DataLog task) {
if (task.isCheckOutTask() && (task.isExecuting() || task.isCancel())) {
String barcodeStr = task.getBarcode();
int lightColor = getHBColor(task.getLightColor());
int outStockType = 0;
if (task.isExecuting()){
outStockType = 2;
} else if (task.isCancel()){
outStockType = 1;
lightColor = 0;
}
lightUpLabel(barcodeStr,
lightColor,
outStockType);
}
}
@Override
public void mergePos(String mergePosUrl, StoragePos storagePos) {
List<String> mergePosList = storagePos.getMergePosList();
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("locationList",mergePosList);
paramMap.put("oneMergedQuantity",mergePosList.size());
log.info("合并料架,请求浩博料架,地址为:"+mergePosUrl+",参数为:"+ JSON.toJSONString(paramMap));
try {
String result = HttpHelper.postJson(mergePosUrl, paramMap);
log.info("合并料架,浩博料架返回结果为:"+result);
} catch (ApiException e) {
log.info("合并料架,浩博料架返回异常,异常信息为:"+e.getMessage());
}
}
@Override
public void splitPos(String splitPosUrl, String splitPosName) {
/**
* {"locationList":["T001A1001"]}
*/
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("locationList",Collections.singletonList(splitPosName));
log.info("拆分料架,请求浩博料架,地址为:"+splitPosUrl+",参数为:"+ JSON.toJSONString(paramMap));
try {
String result = HttpHelper.postJson(splitPosUrl, paramMap);
log.info("拆分料架,浩博料架返回结果为:"+result);
} catch (ApiException e) {
log.info("拆分料架,浩博料架返回异常,异常信息为:"+e.getMessage());
}
}
protected List<String> getInventoryPosName(String cid) {
List<String> hasReelPosNameList = new ArrayList<>();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("shelfCode", cid);
try {
String resultStr = HttpHelper.getParam(getInventoryUrl, null, paramMap);
GetInventoryResponse getInventoryResponse = JSONObject.parseObject(resultStr, GetInventoryResponse.class);
GetInventoryResponse.Result result = getInventoryResponse.getResult();
if (result.isSuccess()) {
List<GetInventoryResponse.DataItem> data = result.getData();
if (data != null && !data.isEmpty()) {
for (GetInventoryResponse.DataItem item : data) {
String externalLocation = item.getExternalLocation();
if (StringUtils.isNotEmpty(externalLocation)){
hasReelPosNameList.add(externalLocation);
}
}
}
}
} catch (ApiException e) {
log.error("获取浩博料架库存信息失败,异常信息为:{}", e.getMessage());
}
return hasReelPosNameList;
}
protected void lightUpLabel(String barcodeStr, int lightColor, int outStockType) {
Map<String, Object> paramMap = new HashMap<>();
String labelIdListJson = "[" + "\'" + barcodeStr + "\'" + "]";
paramMap.put("labelIdListJson", labelIdListJson);
paramMap.put("color", lightColor);
paramMap.put("outStockType", outStockType);
paramMap.put("newShelfCode", "");
paramMap.put("detailsJson", "");
log.info(barcodeStr + ":出库通知,请求浩博料架,地址为:" + lightUpLabelUrl + ",参数为:" + JSON.toJSONString(paramMap));
try {
String result = HttpHelper.postJson(lightUpLabelUrl, paramMap);
log.info(barcodeStr + ":出库通知,浩博料架返回结果为:" + result);
} catch (ApiException e) {
log.info(barcodeStr + ":出库通知,浩博料架返回异常,异常信息为:" + e.getMessage());
}
}
/**
* {
* "controllerIp": "127.0.0.1",
* "port": 5003,
* "ioBoardAddress": 82,
* "channel": 12,
* "isOpen": false
* }
* @param ioBoardAddress
* @param channel
* @param isOpen
*/
protected void controlIoBoard(int ioBoardAddress, int channel, boolean isOpen) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("controllerIp", "127.0.0.1");
paramMap.put("port", 5003);
paramMap.put("ioBoardAddress", ioBoardAddress);
paramMap.put("channel", channel);
paramMap.put("isOpen", isOpen);
try {
log.info("控制IO板请求参数为:" + JSON.toJSONString(paramMap));
String result = HttpHelper.postJson(controlIoBoardUrl, paramMap);
log.info("控制IO板返回结果为:" + result);
} catch (ApiException e) {
log.info("控制IO板异常:", e);
}
}
protected void lightUpSomeLampBeads(List<String> onPosNameList, List<String> offPosNameList) {
// 1. 前置校验:无任何货位数据时直接返回
boolean hasOnData = onPosNameList != null && !onPosNameList.isEmpty();
boolean hasOffData = offPosNameList != null && !offPosNameList.isEmpty();
if (!hasOnData && !hasOffData) {
//log.info("亮灯/灭灯货位列表均为空,无需调用亮灯接口");
return;
}
// 2. 拼接货位项字符串(核心:亮灯color=1,灭灯color=0)
List<String> posItemList = new ArrayList<>();
// 处理亮灯货位(color='1')
if (hasOnData) {
for (String location : onPosNameList) {
if (location != null && !location.trim().isEmpty()) {
posItemList.add("{'color':'1','location':'" + location.trim() + "'}");
}
}
}
// 处理灭灯货位(color='0')
if (hasOffData) {
for (String location : offPosNameList) {
if (location != null && !location.trim().isEmpty()) {
posItemList.add("{'color':'0','location':'" + location.trim() + "'}");
}
}
}
// 3. 封装成指定的JSON格式:{"jsonData":"[货位项1,货位项2]"}
String jsonArray = "["+String.join(",", posItemList)+"]";
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("jsonData", jsonArray);
try {
log.info("亮灯或者灭灯请求参数为:" + JSON.toJSONString(paramMap));
String result = HttpHelper.postJson(lightUpSomeLampBeadsUrl, paramMap);
log.info("亮灯或者灭灯返回参数为:" + result);
} catch (ApiException e) {
log.info("亮灯或者灭灯异常:", e);
}
}
private int getHBColor(String rgb) {
ORDER_COLOR color = ORDER_COLOR.fromRgb(rgb);
if (color == null) {
color = ORDER_COLOR.BLUE;
}
Map<String, Integer> hbColorMap = new HashMap<>();
hbColorMap.put(ORDER_COLOR.BLUE.name(), 3);
hbColorMap.put(ORDER_COLOR.CYAN.name(), 6);
hbColorMap.put(ORDER_COLOR.FIREBRICK.name(), 5);
hbColorMap.put(ORDER_COLOR.FIREBRICK.name(), 2);
hbColorMap.put(ORDER_COLOR.RED.name(), 1);
hbColorMap.put(ORDER_COLOR.YELLOW.name(), 4);
hbColorMap.put(ORDER_COLOR.WHITE.name(), 7);
/*hbColorMap.put(ORDER_COLOR.PURPLE.name(), );
hbColorMap.put(ORDER_COLOR.DARKGREEN.name(), );
hbColorMap.put(ORDER_COLOR.ORANGE.name(), );*/
return hbColorMap.get(color.name());
}
}
package com.neotel.smfcore.custom.haobo;
import com.alibaba.fastjson.JSON;
import com.neotel.smfcore.common.exception.ValidateException;
import com.neotel.smfcore.common.utils.StringUtils;
import com.neotel.smfcore.core.barcode.enums.SOLDER_STATUS;
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.handler.impl.BaseDeviceHandler;
import com.neotel.smfcore.core.device.util.DataCache;
import com.neotel.smfcore.core.order.enums.ORDER_COLOR;
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.bean.OrderSetting;
import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.core.system.util.TaskService;
import com.neotel.smfcore.custom.haobo.bean.request.InStockCallbackRequest;
import com.neotel.smfcore.custom.haobo.bean.request.OutStockCallbackRequest;
import com.neotel.smfcore.custom.haobo.bean.response.HaoBoApiResponse;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
@RequestMapping("/hbNlp")
public class HaoboNlpController {
@Autowired
private TaskService taskService;
@Autowired
private DataCache dataCache;
@Autowired
private IStoragePosManager storagePosManager;
@Autowired
private IBarcodeManager barcodeManager;
@ApiOperation("入库完成回调")
@RequestMapping("/inStockCallback")
@AnonymousAccess
public HaoBoApiResponse inStockCallback(@RequestBody InStockCallbackRequest request) {
log.info("收到入库完成回调信息:" + JSON.toJSONString(request));
DataLog dataLog = null;
Collection<DataLog> queueTasks = taskService.getQueueTasks();
for (DataLog queueTask : queueTasks) {
if (queueTask.getBarcode().equals(request.getLabelId())) {
if (queueTask.isPutInTask() && queueTask.isWait()) {
dataLog = queueTask;
break;
}
}
}
if (dataLog == null) {
return new HaoBoApiResponse(false, "未找到对应的入库任务");
}
StoragePos pos = storagePosManager.getByPosName(request.getLocation());
if (pos == null) {
return new HaoBoApiResponse(false, "未找到对应的库位");
}
if (pos.getBarcode() != null) {
return new HaoBoApiResponse(false, "库位已被占用");
}
Storage storage = dataCache.getStorageById(pos.getStorageId());
dataLog.setCid(storage.getCid());
dataLog.setPosId(pos.getId());
dataLog.setPosName(pos.getPosName());
dataLog.setStorageId(storage.getId());
dataLog.setStorageName(storage.getName());
updatePutInData(dataLog);
return new HaoBoApiResponse(true, "入库完成");
}
@ApiOperation("出库完成回调")
@RequestMapping("/outStockCallback")
@AnonymousAccess
public HaoBoApiResponse outStockCallback(@RequestBody OutStockCallbackRequest request) {
log.info("收到出库完成回调信息:" + JSON.toJSONString(request));
DataLog dataLog = null;
Collection<DataLog> queueTasks = taskService.getQueueTasks();
for (DataLog queueTask : queueTasks) {
if (queueTask.getBarcode().equals(request.getLabelId())) {
if (queueTask.isCheckOutTask() && (queueTask.isWait() || queueTask.isExecuting())) {
dataLog = queueTask;
break;
}
}
}
if (dataLog != null) {
updateCheckoutData(dataLog, OP_STATUS.FINISHED);
return new HaoBoApiResponse(true, "出库完成");
} else {
//判断在不在库位中,如果不在库位中,则忽略
StoragePos pos = storagePosManager.getByBarcode(request.getLabelId());
if (pos == null) {
if (StringUtils.isNotEmpty(request.getLocation())) {
pos = storagePosManager.getByPosName(request.getLocation());
}
}
if (pos == null || pos.getBarcode() == null) {
log.info("根据库位名称和条码都未找到对应的库存信息,库位名称为:{},唯一码为:{}", request.getLocation(), request.getLabelId());
return new HaoBoApiResponse(true, "根据唯一码或者库位号都未找到对应的库存信息,默认已出库");
}
Storage storage = dataCache.getStorageById(pos.getStorageId());
DataLog createDataLog = new DataLog(storage, pos.getBarcode(), pos);
createDataLog.setType(OP.CHECKOUT);
createDataLog.setOperator("system");
createDataLog = taskService.updateQueueTask(createDataLog);
updateCheckoutData(createDataLog, OP_STATUS.FINISHED);
return new HaoBoApiResponse(true, "出库完成");
}
}
/**
* 入仓位完成
*/
protected void updatePutInData(DataLog task) throws ValidateException {
//从队列里面移除操作
taskService.removeQueueTask(task);
StoragePos storagePos = storagePosManager.get(task.getPosId());
//二维码状态
Barcode barcode = barcodeManager.findByBarcode(task.getBarcode());
if (barcode != null) {
barcode.setUsedCount(barcode.getUsedCount() + 1);
barcode.setPutInTime(System.currentTimeMillis());
barcode.updateSluggishTime(dataCache.getPNsluggishDay(barcode.getPartNumber()));
barcode.setInOpor(task.getOperator());
barcode.setCheckOutDate(null, "");
barcode.setPosName(task.getPosName());
if (barcode.isSolder()) {
if (storagePos.isWarmPos()) {
//回温仓位
barcode.setSolderStatus(SOLDER_STATUS.RETREAT_STORAGE.name());
} else {
barcode.setSolderStatus(SOLDER_STATUS.UNDER_REFRIGERATION.name());
}
barcode.setNeedOutDate(null);
}
barcodeManager.save(barcode);
}
storagePos.setBarcode(barcode);
storagePos.setUsed(true);
storagePos.setCanCheckOutTime(System.currentTimeMillis());
storagePosManager.saveMergePos(storagePos);
if (barcode != null) {
dataCache.updateInventory(storagePos, barcode);
//记录日志,完成 task
task.setBatchInfo(barcode.getBatch());
task.setNum(barcode.getAmount());
task.setProviderNumber(barcode.getProviderNumber());
}
//更新缓存中的库存信息
task.setStatus(OP_STATUS.FINISHED.name());
taskService.updateFinishedTask(task);
}
/**
* 出仓位完成
*/
private void updateCheckoutData(DataLog task, OP_STATUS outBoxStatus) throws ValidateException {
//从队列里面移除操作
taskService.removeQueueTask(task);
StoragePos storagePos = storagePosManager.get(task.getPosId());
Barcode barcode = storagePos.getBarcode();
if (barcode == null) {
log.warn("任务:" + task.getId() + " 仓位:" + task.getPosId() + " 的 Barcode 为null, 之前可能处理过,结束任务后直接返回");
//记录日志
task.setStatus(outBoxStatus.name());
taskService.updateFinishedTask(task);
return;
}
Barcode barcodeDB = barcodeManager.get(barcode.getId());
if (barcodeDB != null) {
//二维码状态
barcodeDB.setUsed(true);
barcodeDB.setUsedDate(new Date());
//仓位状态
barcodeDB.setCheckOutDate(new Date(), task.getOperator());
barcodeDB.setPosName("");
barcodeManager.save(barcodeDB);
task.setBatchInfo(barcodeDB.getBatch());
//记录在库时长
task.setInStoreTime(barcodeDB.getInStoreMiniute());
}
storagePos.setBarcode(null);
storagePos.setUsed(false);
storagePosManager.saveMergePos(storagePos);
log.info("出库完成,清空仓位: " + storagePos.getId() + "[" + storagePos.getPosName() + "]");
//更新缓存中的库存信息
dataCache.updateInventory(storagePos, barcode);
// 调用西门子接口
// SiemensApi.lotInOut(barcode.getBarcode(),2);
//记录日志
task.setStatus(outBoxStatus.name());
taskService.updateFinishedTask(task);
}
}
package com.neotel.smfcore.custom.haobo.bean;
import lombok.Data;
import java.util.List;
@Data
public class PosDiff {
List<String> inventoryOnly;
List<String> usedOnly;
public PosDiff(List<String> inventoryOnly, List<String> usedOnly) {
this.inventoryOnly = inventoryOnly;
this.usedOnly = usedOnly;
}
}
package com.neotel.smfcore.custom.haobo.bean;
import java.util.List;
public class PosTotalDiffResult {
// 所有新增(多的)货位列表
private List<String> totalAddList;
// 所有减少(少的)货位列表
private List<String> totalReduceList;
public PosTotalDiffResult(List<String> totalAddList, List<String> totalReduceList) {
this.totalAddList = totalAddList;
this.totalReduceList = totalReduceList;
}
// Getter方法(用于外部获取返回值)
public List<String> getTotalAddList() {
return totalAddList;
}
public List<String> getTotalReduceList() {
return totalReduceList;
}
}
package com.neotel.smfcore.custom.haobo.bean.request;
import lombok.Data;
@Data
public class InStockCallbackRequest {
private String labelId;
private String location;
private String detailsJson;
}
package com.neotel.smfcore.custom.haobo.bean.request;
import lombok.Data;
@Data
public class OutStockCallbackRequest {
private String labelId;
private String location;
private String detailsJson;
}
package com.neotel.smfcore.custom.haobo.bean.response;
import lombok.Data;
import java.util.List;
@Data
public class GetInventoryResponse {
private Result result;
private String targetUrl;
private boolean success;
private Object error;
private boolean unAuthorizedRequest;
@Data
public static class Result {
private boolean success;
private String message;
private List<DataItem> data;
}
@Data
public static class DataItem {
private String location;
private String labelId;
private String shelfCode;
private String operationTime;
private int status;
private String externalLocation;
private String presentInductionLocation;
private String transferShelfCode;
private String inStockDetailsJson;
private String outStockDetailsJson;
private boolean inStockCallbackSuccess;
private int inStockCallbackNum;
private boolean outStockCallbackSuccess;
private int outStockCallbackNum;
private String updateTime;
private long hardwareTime;
private String materialCode;
private String materialName;
private String materialSpec;
private Integer materialQuantity;
private String productionDate;
private String timeout;
private int id;
}
}
\ No newline at end of file \ No newline at end of file
package com.neotel.smfcore.custom.haobo.bean.response;
import lombok.Data;
@Data
public class HaoBoApiResponse {
private boolean success;
private String message;
private Object data;
public HaoBoApiResponse(boolean success,String message) {
this.message = message;
this.success = success;
this.data = null;
}
}
...@@ -463,4 +463,11 @@ smfcore.reelPosMove.posIsNull=\u5E93\u4F4D[{1}]\u4E3A\u7A7A ...@@ -463,4 +463,11 @@ smfcore.reelPosMove.posIsNull=\u5E93\u4F4D[{1}]\u4E3A\u7A7A
smfcore.liteOrder.exist=\u9700\u6C42\u5355{0}\u5DF2\u5B58\u5728 smfcore.liteOrder.exist=\u9700\u6C42\u5355{0}\u5DF2\u5B58\u5728
smfcore.reelPosMove.barcodeError=\u5E93\u4F4D{0}\u6761\u7801\u4E3A{1}\uFF0C\u4E0E\u79FB\u5E93\u6761\u7801{1}\u4E0D\u4E00\u81F4 smfcore.reelPosMove.barcodeError=\u5E93\u4F4D{0}\u6761\u7801\u4E3A{1}\uFF0C\u4E0E\u79FB\u5E93\u6761\u7801{1}\u4E0D\u4E00\u81F4
smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u4FE1\u606F smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u4FE1\u606F
smfcore.pos.merge.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.merge.numError=\u8BF7\u9009\u62E9[{0}]\u4E2A\u8981\u5408\u5E76\u7684\u5E93\u4F4D
smfcore.pos.merge.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u5408\u5E76
smfcore.pos.merge.hasMergePos=\u5E93\u4F4D[{0}]\u5DF2\u5408\u5E76,\u4E0D\u80FD\u7EE7\u7EED\u5408\u5E76
smfcore.pos.merge.notAdjacent=\u8BF7\u9009\u62E9\u76F8\u90BB\u7684\u5E93\u4F4D\u5408\u5E76
smfcore.pos.split.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.split.notMergePos=\u5E93\u4F4D[{0}]\u672A\u5408\u5E76,\u4E0D\u80FD\u62C6\u5206
smfcore.pos.split.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u62C6\u5206
...@@ -453,3 +453,11 @@ smfcore.liteOrder.exist=Bedarfsauftrag {0} existiert bereits ...@@ -453,3 +453,11 @@ smfcore.liteOrder.exist=Bedarfsauftrag {0} existiert bereits
smfcore.reelPosMove.barcodeError=Der Barcode des Lagerorts {0} ist {1}, nicht identisch mit dem Umzugscode {1} smfcore.reelPosMove.barcodeError=Der Barcode des Lagerorts {0} ist {1}, nicht identisch mit dem Umzugscode {1}
smfcore.barcode.notFindFeerder=Stationsinformation [{0}] nicht gefunden smfcore.barcode.notFindFeerder=Stationsinformation [{0}] nicht gefunden
smfcore.storagePos.provider=Lieferant smfcore.storagePos.provider=Lieferant
smfcore.pos.merge.notNull=Lagerposition darf nicht leer sein
smfcore.pos.merge.numError=W\u00E4hlen Sie [{0}] Lagerpositionen zum Zusammenf\u00FChren aus
smfcore.pos.merge.hasMaterial=Lagerposition [{0}] enth\u00E4lt Materialien und kann nicht zusammengef\u00FChrt werden
smfcore.pos.merge.hasMergePos=Lagerposition [{0}] wurde bereits zusammengef\u00FChrt und kann nicht weiter zusammengef\u00FChrt werden
smfcore.pos.merge.notAdjacent=W\u00E4hlen Sie benachbarte Lagerpositionen zum Zusammenf\u00FChren aus
smfcore.pos.split.notNull=Lagerposition darf nicht leer sein
smfcore.pos.split.notMergePos=Lagerposition [{0}] wurde nicht zusammengef\u00FChrt und kann nicht aufgeteilt werden
smfcore.pos.split.hasMaterial=Lagerposition [{0}] enth\u00E4lt Materialien und kann nicht aufgeteilt werden
...@@ -454,3 +454,11 @@ smfcore.liteOrder.exist=Requirement order {0} already exists ...@@ -454,3 +454,11 @@ smfcore.liteOrder.exist=Requirement order {0} already exists
smfcore.reelPosMove.barcodeError=Location {0} barcode is {1}, inconsistent with the transfer barcode {1} smfcore.reelPosMove.barcodeError=Location {0} barcode is {1}, inconsistent with the transfer barcode {1}
smfcore.barcode.notFindFeerder=Station information [{0}] not found smfcore.barcode.notFindFeerder=Station information [{0}] not found
smfcore.storagePos.provider=Provider smfcore.storagePos.provider=Provider
smfcore.pos.merge.notNull=Storage position cannot be empty
smfcore.pos.merge.numError=Please select [{0}] storage positions to merge
smfcore.pos.merge.hasMaterial=Storage position [{0}] contains materials and cannot be merged
smfcore.pos.merge.hasMergePos=Storage position [{0}] has been merged and cannot be merged further
smfcore.pos.merge.notAdjacent=Please select adjacent storage positions to merge
smfcore.pos.split.notNull=Storage position cannot be empty
smfcore.pos.split.notMergePos=Storage position [{0}] has not been merged and cannot be split
smfcore.pos.split.hasMaterial=Storage position [{0}] contains materials and cannot be split
...@@ -453,3 +453,11 @@ smfcore.liteOrder.exist=Le bon de commande {0} existe d\u00E9j\u00E0 ...@@ -453,3 +453,11 @@ smfcore.liteOrder.exist=Le bon de commande {0} existe d\u00E9j\u00E0
smfcore.reelPosMove.barcodeError=Le code-barres de l'emplacement {0} est {1}, incompatible avec le code-barres de transfert {1} smfcore.reelPosMove.barcodeError=Le code-barres de l'emplacement {0} est {1}, incompatible avec le code-barres de transfert {1}
smfcore.barcode.notFindFeerder=Informations de station [{0}] non trouv\u00E9es smfcore.barcode.notFindFeerder=Informations de station [{0}] non trouv\u00E9es
smfcore.storagePos.provider=Fournisseur smfcore.storagePos.provider=Fournisseur
smfcore.pos.merge.notNull=L'emplacement de stockage ne peut pas \u00EAtre vide
smfcore.pos.merge.numError=Veuillez s\u00E9lectionner [{0}] emplacements de stockage \u00E0 fusionner
smfcore.pos.merge.hasMaterial=L'emplacement de stockage [{0}] contient des mati\u00E8res et ne peut pas \u00EAtre fusionn\u00E9
smfcore.pos.merge.hasMergePos=L'emplacement de stockage [{0}] a d\u00E9j\u00E0 \u00E9t\u00E9 fusionn\u00E9 et ne peut pas \u00EAtre fusionn\u00E9 davantage
smfcore.pos.merge.notAdjacent=Veuillez s\u00E9lectionner des emplacements de stockage adjacents \u00E0 fusionner
smfcore.pos.split.notNull=L'emplacement de stockage ne peut pas \u00EAtre vide
smfcore.pos.split.notMergePos=L'emplacement de stockage [{0}] n'a pas \u00E9t\u00E9 fusionn\u00E9 et ne peut pas \u00EAtre divis\u00E9
smfcore.pos.split.hasMaterial=L'emplacement de stockage [{0}] contient des mati\u00E8res et ne peut pas \u00EAtre divis\u00E9
...@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u8981\u6C42\u5358 {0} \u306F\u65E2\u306B\u5B58\u5728\u3 ...@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u8981\u6C42\u5358 {0} \u306F\u65E2\u306B\u5B58\u5728\u3
smfcore.reelPosMove.barcodeError=\u5EAB\u4F4D {0} \u306E\u30D0\u30FC\u30B3\u30FC\u30C9\u306F {1} \u3067\u3001\u79FB\u5EAB\u30D0\u30FC\u30B3\u30FC\u30C9 {1} \u3068\u4E0D\u4E00\u81F4\u3067\u3059 smfcore.reelPosMove.barcodeError=\u5EAB\u4F4D {0} \u306E\u30D0\u30FC\u30B3\u30FC\u30C9\u306F {1} \u3067\u3001\u79FB\u5EAB\u30D0\u30FC\u30B3\u30FC\u30C9 {1} \u3068\u4E0D\u4E00\u81F4\u3067\u3059
smfcore.barcode.notFindFeerder=[{0}]\u30B9\u30C6\u30FC\u30B7\u30E7\u30F3\u60C5\u5831\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 smfcore.barcode.notFindFeerder=[{0}]\u30B9\u30C6\u30FC\u30B7\u30E7\u30F3\u60C5\u5831\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
smfcore.storagePos.provider=\u4F9B\u7D66\u8005 smfcore.storagePos.provider=\u4F9B\u7D66\u8005
smfcore.pos.merge.notNull=\u68DA\u4F4D\u7F6E\u3092\u7A7A\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093
smfcore.pos.merge.numError=\u7D71\u5408\u3059\u308B\u68DA\u4F4D\u7F6E\u3092[{0}]\u3064\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044
smfcore.pos.merge.hasMaterial=\u68DA\u4F4D\u7F6E[{0}]\u306B\u8CC7\u6750\u304C\u3042\u308B\u305F\u3081\u3001\u7D71\u5408\u3067\u304D\u307E\u305B\u3093
smfcore.pos.merge.hasMergePos=\u68DA\u4F4D\u7F6E[{0}]\u306F\u65E2\u306B\u7D71\u5408\u3055\u308C\u3066\u3044\u308B\u305F\u3081\u3001\u3055\u3089\u306B\u7D71\u5408\u3067\u304D\u307E\u305B\u3093
smfcore.pos.merge.notAdjacent=\u96A3\u63A5\u3059\u308B\u68DA\u4F4D\u7F6E\u3092\u9078\u629E\u3057\u3066\u7D71\u5408\u3057\u3066\u304F\u3060\u3055\u3044
smfcore.pos.split.notNull=\u68DA\u4F4D\u7F6E\u3092\u7A7A\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093
smfcore.pos.split.notMergePos=\u68DA\u4F4D\u7F6E[{0}]\u306F\u7D71\u5408\u3055\u308C\u3066\u3044\u306A\u3044\u305F\u3081\u3001\u5206\u5272\u3067\u304D\u307E\u305B\u3093
smfcore.pos.split.hasMaterial=\u68DA\u4F4D\u7F6E[{0}]\u306B\u8CC7\u6750\u304C\u3042\u308B\u305F\u3081\u3001\u5206\u5272\u3067\u304D\u307E\u305B\u3093
...@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u9700\u6C42\u5355{0}\u5DF2\u5B58\u5728 ...@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u9700\u6C42\u5355{0}\u5DF2\u5B58\u5728
smfcore.reelPosMove.barcodeError=\u5E93\u4F4D{0}\u6761\u7801\u4E3A{1}\uFF0C\u4E0E\u79FB\u5E93\u6761\u7801{1}\u4E0D\u4E00\u81F4 smfcore.reelPosMove.barcodeError=\u5E93\u4F4D{0}\u6761\u7801\u4E3A{1}\uFF0C\u4E0E\u79FB\u5E93\u6761\u7801{1}\u4E0D\u4E00\u81F4
smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u4FE1\u606F smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u4FE1\u606F
smfcore.storagePos.provider=\u4F9B\u5E94\u5546 smfcore.storagePos.provider=\u4F9B\u5E94\u5546
smfcore.pos.merge.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.merge.numError=\u8BF7\u9009\u62E9[{0}]\u4E2A\u8981\u5408\u5E76\u7684\u5E93\u4F4D
smfcore.pos.merge.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u5408\u5E76
smfcore.pos.merge.hasMergePos=\u5E93\u4F4D[{0}]\u5DF2\u5408\u5E76,\u4E0D\u80FD\u7EE7\u7EED\u5408\u5E76
smfcore.pos.merge.notAdjacent=\u8BF7\u9009\u62E9\u76F8\u90BB\u7684\u5E93\u4F4D\u5408\u5E76
smfcore.pos.split.notNull=\u5E93\u4F4D\u4E0D\u80FD\u4E3A\u7A7A
smfcore.pos.split.notMergePos=\u5E93\u4F4D[{0}]\u672A\u5408\u5E76,\u4E0D\u80FD\u62C6\u5206
smfcore.pos.split.hasMaterial=\u5E93\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u62C6\u5206
...@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u9700\u6C42\u55AE {0} \u5DF2\u5B58\u5728 ...@@ -450,3 +450,11 @@ smfcore.liteOrder.exist=\u9700\u6C42\u55AE {0} \u5DF2\u5B58\u5728
smfcore.reelPosMove.barcodeError=\u5EAB\u4F4D {0} \u689D\u78BC\u70BA {1}\uFF0C\u8207\u79FB\u5EAB\u689D\u78BC {1} \u4E0D\u4E00\u81F4 smfcore.reelPosMove.barcodeError=\u5EAB\u4F4D {0} \u689D\u78BC\u70BA {1}\uFF0C\u8207\u79FB\u5EAB\u689D\u78BC {1} \u4E0D\u4E00\u81F4
smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u8CC7\u8A0A smfcore.barcode.notFindFeerder=\u672A\u627E\u5230[{0}]\u7AD9\u4F4D\u8CC7\u8A0A
smfcore.storagePos.provider=\u4F9B\u7D66\u8005 smfcore.storagePos.provider=\u4F9B\u7D66\u8005
smfcore.pos.merge.notNull=\u5EAB\u4F4D\u4E0D\u80FD\u70BA\u7A7A
smfcore.pos.merge.numError=\u8ACB\u9078\u64C7[{0}]\u500B\u8981\u5408\u4F75\u7684\u5EAB\u4F4D
smfcore.pos.merge.hasMaterial=\u5EAB\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u5408\u4F75
smfcore.pos.merge.hasMergePos=\u5EAB\u4F4D[{0}]\u5DF2\u5408\u4F75,\u4E0D\u80FD\u7E7C\u7E8C\u5408\u4F75
smfcore.pos.merge.notAdjacent=\u8ACB\u9078\u64C7\u76F8\u9130\u7684\u5EAB\u4F4D\u5408\u4F75
smfcore.pos.split.notNull=\u5EAB\u4F4D\u4E0D\u80FD\u70BA\u7A7A
smfcore.pos.split.notMergePos=\u5EAB\u4F4D[{0}]\u672A\u5408\u4F75,\u4E0D\u80FD\u62C6\u5206
smfcore.pos.split.hasMaterial=\u5EAB\u4F4D[{0}]\u6709\u7269\u6599,\u4E0D\u80FD\u62C6\u5206
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!