|
|
@ -61,6 +61,194 @@ public class DashboardPushTask { |
|
|
@Autowired(required = false) |
|
|
@Autowired(required = false) |
|
|
private TuskClientService tuskClientService; |
|
|
private TuskClientService tuskClientService; |
|
|
|
|
|
|
|
|
|
|
|
@Value("${custom.wcs-url}") |
|
|
|
|
|
private String wcsUrl; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 每10秒检查人工拣选数据并推送 |
|
|
|
|
|
* |
|
|
|
|
|
* <p><b>数据来源:</b>WCS系统 - /api/wms/query-auto-sorting-info</p> |
|
|
|
|
|
* |
|
|
|
|
|
* <p><b>数据说明:</b></p> |
|
|
|
|
|
* <ul> |
|
|
|
|
|
* <li>sortingStation=1043 - 第一个表格(人工拣选1)</li> |
|
|
|
|
|
* <li>sortingStation=1028 - 第二个表格(人工拣选2)</li> |
|
|
|
|
|
* </ul> |
|
|
|
|
|
* |
|
|
|
|
|
* <p><b>配置开关:</b></p> |
|
|
|
|
|
* <ul> |
|
|
|
|
|
* <li>dashboard.push.enabled - 看板推送总开关</li> |
|
|
|
|
|
* </ul> |
|
|
|
|
|
*/ |
|
|
|
|
|
@Scheduled(fixedRate = 10000) |
|
|
|
|
|
public void pushManualPickingData() { |
|
|
|
|
|
// 检查总开关 |
|
|
|
|
|
if (!dashboardPushEnabled) { |
|
|
|
|
|
log.trace("看板推送已禁用"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
log.debug("=== 开始获取人工拣选看板数据 ==="); |
|
|
|
|
|
|
|
|
|
|
|
// 从WCS获取人工拣选数据 |
|
|
|
|
|
Map<String, Object> data = getManualPickingDataFromWcs(); |
|
|
|
|
|
|
|
|
|
|
|
// 如果返回null,转换为空数据 |
|
|
|
|
|
if (data == null) { |
|
|
|
|
|
data = createEmptyManualPickingData(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 计算数据哈希值并推送 |
|
|
|
|
|
int currentHash = data.hashCode(); |
|
|
|
|
|
webSocketService.pushManualPickingData(data); |
|
|
|
|
|
lastDataHash.put("manual-picking", currentHash); |
|
|
|
|
|
|
|
|
|
|
|
log.debug("=== 人工拣选数据推送完成 ==="); |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("推送人工拣选数据失败,推送空数据清空前端列表: {}", e.getMessage(), e); |
|
|
|
|
|
// 异常时推送空数据,避免前端显示过期数据 |
|
|
|
|
|
try { |
|
|
|
|
|
Map<String, Object> emptyData = createEmptyManualPickingData(); |
|
|
|
|
|
webSocketService.pushManualPickingData(emptyData); |
|
|
|
|
|
lastDataHash.put("manual-picking", emptyData.hashCode()); |
|
|
|
|
|
} catch (Exception ex) { |
|
|
|
|
|
log.error("推送空数据失败: {}", ex.getMessage()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 从WCS获取人工拣选数据 |
|
|
|
|
|
* |
|
|
|
|
|
* @return 人工拣选数据 |
|
|
|
|
|
*/ |
|
|
|
|
|
private Map<String, Object> getManualPickingDataFromWcs() { |
|
|
|
|
|
try { |
|
|
|
|
|
String url = wcsUrl + "query-auto-sorting-info"; |
|
|
|
|
|
ObjectMapper mapper = new ObjectMapper(); |
|
|
|
|
|
|
|
|
|
|
|
// 请求1043站数据 |
|
|
|
|
|
log.info("调用WCS人工拣选API (1043站): {}", url); |
|
|
|
|
|
List<Map<String, Object>> station1043List = getStationData(url, "1043", mapper); |
|
|
|
|
|
log.info("1043站数据获取完成: {}条", station1043List.size()); |
|
|
|
|
|
|
|
|
|
|
|
// 请求1028站数据 |
|
|
|
|
|
log.info("调用WCS人工拣选API (1028站): {}", url); |
|
|
|
|
|
List<Map<String, Object>> station1028List = getStationData(url, "1028", mapper); |
|
|
|
|
|
log.info("1028站数据获取完成: {}条", station1028List.size()); |
|
|
|
|
|
|
|
|
|
|
|
// 构造返回数据 |
|
|
|
|
|
Map<String, Object> resultData = new HashMap<>(); |
|
|
|
|
|
resultData.put("station1043List", station1043List); |
|
|
|
|
|
resultData.put("station1028List", station1028List); |
|
|
|
|
|
|
|
|
|
|
|
log.info("=== 人工拣选数据处理完成 === 1043站:{}条, 1028站:{}条", |
|
|
|
|
|
station1043List.size(), station1028List.size()); |
|
|
|
|
|
|
|
|
|
|
|
return resultData; |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("从WCS获取人工拣选数据失败: {}", e.getMessage(), e); |
|
|
|
|
|
return null; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 获取指定工作站的数据 |
|
|
|
|
|
* |
|
|
|
|
|
* @param url WCS API地址 |
|
|
|
|
|
* @param devCode 设备编码(工作站号) |
|
|
|
|
|
* @param mapper JSON解析器 |
|
|
|
|
|
* @return 工作站数据列表 |
|
|
|
|
|
*/ |
|
|
|
|
|
private List<Map<String, Object>> getStationData(String url, String devCode, ObjectMapper mapper) { |
|
|
|
|
|
List<Map<String, Object>> stationList = new ArrayList<>(); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// 构造POST请求参数 - 直接传devCode字符串 |
|
|
|
|
|
String jsonBody = "\"" + devCode + "\""; |
|
|
|
|
|
|
|
|
|
|
|
String wcsResponse = HttpUtils.doPost(url, jsonBody, null); |
|
|
|
|
|
log.debug("WCS API返回数据 ({}站): {}", devCode, wcsResponse); |
|
|
|
|
|
|
|
|
|
|
|
// 解析JSON数据 |
|
|
|
|
|
JsonNode rootNode = mapper.readTree(wcsResponse); |
|
|
|
|
|
|
|
|
|
|
|
// 获取sortingStations数组 |
|
|
|
|
|
if (!rootNode.has("sortingStations")) { |
|
|
|
|
|
log.warn("WCS返回数据中没有sortingStations ({}站)", devCode); |
|
|
|
|
|
return stationList; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
JsonNode sortingStations = rootNode.get("sortingStations"); |
|
|
|
|
|
|
|
|
|
|
|
// 处理返回的数据 |
|
|
|
|
|
for (JsonNode station : sortingStations) { |
|
|
|
|
|
String sortingStation = station.get("sortingStation").asText(); |
|
|
|
|
|
JsonNode materials = station.get("materials"); |
|
|
|
|
|
|
|
|
|
|
|
// 只处理匹配的工作站数据 |
|
|
|
|
|
if (devCode.equals(sortingStation) && materials != null && materials.isArray()) { |
|
|
|
|
|
for (JsonNode material : materials) { |
|
|
|
|
|
Map<String, Object> item = convertWcsMaterialToPickingItem(material); |
|
|
|
|
|
stationList.add(item); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.error("获取{}站数据失败: {}", devCode, e.getMessage(), e); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return stationList; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 将WCS返回的material数据转换为人工拣选前端需要的格式 |
|
|
|
|
|
* |
|
|
|
|
|
* @param material WCS返回的物料数据 |
|
|
|
|
|
* @return 前端表格数据格式 |
|
|
|
|
|
*/ |
|
|
|
|
|
private Map<String, Object> convertWcsMaterialToPickingItem(JsonNode material) { |
|
|
|
|
|
Map<String, Object> item = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
// 拣选托盘码 (源托盘) |
|
|
|
|
|
item.put("pickingBatchNo", material.has("sourcePalletCode") ? |
|
|
|
|
|
material.get("sourcePalletCode").asText() : ""); |
|
|
|
|
|
|
|
|
|
|
|
// 拣选物料名称 (SKU) |
|
|
|
|
|
item.put("pickingMaterialName", material.has("sku") ? |
|
|
|
|
|
material.get("sku").asText() : ""); |
|
|
|
|
|
|
|
|
|
|
|
// RFID条码 |
|
|
|
|
|
item.put("rfidBarcode", material.has("rfidBarcode") ? |
|
|
|
|
|
material.get("rfidBarcode").asText() : ""); |
|
|
|
|
|
|
|
|
|
|
|
// 状态 (根据isCompleted判断) |
|
|
|
|
|
boolean isCompleted = material.has("isCompleted") && material.get("isCompleted").asBoolean(); |
|
|
|
|
|
item.put("status", isCompleted ? "完成" : "等待分拣"); |
|
|
|
|
|
|
|
|
|
|
|
// 存放位置 (工作站编号) |
|
|
|
|
|
item.put("storageLocation", material.has("sortingStation") ? |
|
|
|
|
|
material.get("sortingStation").asText() : ""); |
|
|
|
|
|
|
|
|
|
|
|
return item; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 创建空的人工拣选数据 |
|
|
|
|
|
* |
|
|
|
|
|
* @return 空的人工拣选数据结构 |
|
|
|
|
|
*/ |
|
|
|
|
|
private Map<String, Object> createEmptyManualPickingData() { |
|
|
|
|
|
Map<String, Object> emptyData = new HashMap<>(); |
|
|
|
|
|
emptyData.put("station1043List", new ArrayList<>()); |
|
|
|
|
|
emptyData.put("station1028List", new ArrayList<>()); |
|
|
|
|
|
return emptyData; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 每5秒检查机械臂拣选数据并推送 |
|
|
* 每5秒检查机械臂拣选数据并推送 |
|
|
* |
|
|
* |
|
|
|