Commit 9dd7f709 张少辉

1.库位异常亮灯指令

1 个父辈 63ec2006
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.util.DevicesStatusUtil;
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;
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);
@PostConstruct
private void init() {
String apiName = smfApi.getApiName();
if (haoboApi.isForThisApi(apiName)) {
scheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
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; package com.neotel.smfcore.custom.haobo;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.neotel.smfcore.common.exception.ApiException; import com.neotel.smfcore.common.exception.ApiException;
import com.neotel.smfcore.common.utils.DateUtil; import com.neotel.smfcore.common.utils.DateUtil;
import com.neotel.smfcore.common.utils.HttpHelper; import com.neotel.smfcore.common.utils.HttpHelper;
import com.neotel.smfcore.core.api.listener.BaseSmfApiListener; 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.order.enums.ORDER_COLOR;
import com.neotel.smfcore.core.system.service.po.DataLog; import com.neotel.smfcore.core.system.service.po.DataLog;
import com.neotel.smfcore.custom.haobo.bean.response.GetInventoryResponse;
import lombok.extern.slf4j.Slf4j; 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 org.springframework.stereotype.Service;
import java.util.Arrays; import javax.annotation.PostConstruct;
import java.util.HashMap; import java.util.*;
import java.util.Map;
@Slf4j @Slf4j
@Service @Service
public class HaoboApi extends BaseSmfApiListener { public class HaoboApi extends BaseSmfApiListener {
@Value("${api.getInventoryUrl:}")
private String getInventoryUrl = "";
@Value("${api.lightUpLabelUrl:}")
private String lightUpLabelUrl ="";
@Value("${api.lightUpSomeLampBeadsUrl:}")
private String lightUpSomeLampBeadsUrl ="";
@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);
}
@Override @Override
public boolean isForThisApi(String apiName) { public boolean isForThisApi(String apiName) {
return "haobo".equalsIgnoreCase(apiName); return "haobo".equalsIgnoreCase(apiName);
...@@ -44,25 +68,99 @@ public class HaoboApi extends BaseSmfApiListener { ...@@ -44,25 +68,99 @@ public class HaoboApi extends BaseSmfApiListener {
@Override @Override
public void outTaskStatusChange(String outNotifyUrl, DataLog task) { public void outTaskStatusChange(String outNotifyUrl, DataLog task) {
if (task.isCheckOutTask() && (task.isExecuting() || task.isCancel())) { if (task.isCheckOutTask() && (task.isExecuting() || task.isCancel())) {
Map<String, Object> paramMap = new HashMap<>();
String labelIdListJson = "[" + "\'" + task.getBarcode() + "\'" + "]"; String barcodeStr = task.getBarcode();
paramMap.put("labelIdListJson", labelIdListJson); int lightColor = getHBColor(task.getLightColor());
if (task.isExecuting()) { int outStockType = 0;
paramMap.put("color", getHBColor(task.getLightColor())); if (task.isExecuting()){
paramMap.put("outStockType", 2); outStockType = 2;
} else if (task.isCancel()) { } else if (task.isCancel()){
paramMap.put("color", 0); outStockType = 1;
paramMap.put("outStockType", 1); lightColor = 0;
} }
paramMap.put("newShelfCode", "");
paramMap.put("detailsJson", ""); lightUpLabel(barcodeStr,
log.info(task.getBarcode() + ":出库通知,请求浩博料架,地址为:" + outNotifyUrl + ",参数为:" + JSON.toJSONString(paramMap)); lightColor,
try { outStockType);
String result = HttpHelper.postJson(outNotifyUrl, paramMap); }
log.info(task.getBarcode() + ":出库通知,浩博料架返回结果为:" + result); }
} catch (ApiException e) {
log.info(task.getBarcode() + ":出库通知,浩博料架返回异常,异常信息为:" + 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) {
hasReelPosNameList.add(item.getExternalLocation());
}
}
} }
} 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());
}
}
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);
} }
} }
......
...@@ -109,7 +109,9 @@ public class HaoboNlpController { ...@@ -109,7 +109,9 @@ public class HaoboNlpController {
//判断在不在库位中,如果不在库位中,则忽略 //判断在不在库位中,如果不在库位中,则忽略
StoragePos pos = storagePosManager.getByBarcode(request.getLabelId()); StoragePos pos = storagePosManager.getByBarcode(request.getLabelId());
if (pos == null) { if (pos == null) {
pos = storagePosManager.getByPosName(request.getLocation()); if (StringUtils.isNotEmpty(request.getLocation())) {
pos = storagePosManager.getByPosName(request.getLocation());
}
} }
if (pos == null || pos.getBarcode() == null) { if (pos == null || pos.getBarcode() == null) {
log.info("根据库位名称和条码都未找到对应的库存信息,库位名称为:{},唯一码为:{}", request.getLocation(), request.getLabelId()); log.info("根据库位名称和条码都未找到对应的库存信息,库位名称为:{},唯一码为:{}", request.getLocation(), request.getLabelId());
...@@ -118,7 +120,7 @@ public class HaoboNlpController { ...@@ -118,7 +120,7 @@ public class HaoboNlpController {
Storage storage = dataCache.getStorageById(pos.getStorageId()); Storage storage = dataCache.getStorageById(pos.getStorageId());
DataLog createDataLog = new DataLog(storage, pos.getBarcode(), pos); DataLog createDataLog = new DataLog(storage, pos.getBarcode(), pos);
createDataLog.setType(OP.CHECKOUT); createDataLog.setType(OP.CHECKOUT);
createDataLog.setOperator("NLP"); createDataLog.setOperator("system");
createDataLog = taskService.updateQueueTask(createDataLog); createDataLog = taskService.updateQueueTask(createDataLog);
updateCheckoutData(createDataLog, OP_STATUS.FINISHED); updateCheckoutData(createDataLog, OP_STATUS.FINISHED);
return new HaoBoApiResponse(true, "出库完成"); return new HaoBoApiResponse(true, "出库完成");
......
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.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
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!