|
|
|
@ -1,8 +1,12 @@ |
|
|
|
package com.gaotao.modules.dashboard.task; |
|
|
|
|
|
|
|
import com.beust.ah.A; |
|
|
|
import com.fasterxml.jackson.databind.JsonNode; |
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper; |
|
|
|
import com.gaotao.common.utils.HttpUtils; |
|
|
|
import com.gaotao.modules.automatedWarehouse.entity.tusk.AgvStatus; |
|
|
|
import com.gaotao.modules.automatedWarehouse.entity.tusk.TuskResponse; |
|
|
|
import com.gaotao.modules.automatedWarehouse.service.TuskClientService; |
|
|
|
import com.gaotao.modules.dashboard.service.DashboardWebSocketService; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
@ -46,10 +50,13 @@ public class DashboardPushTask { |
|
|
|
* 上次推送的数据哈希值(用于检测数据变更) |
|
|
|
*/ |
|
|
|
private Map<String, Integer> lastDataHash = new HashMap<>(); |
|
|
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
private com.gaotao.modules.dashboard.dao.DashboardDao dashboardDao; |
|
|
|
|
|
|
|
@Autowired(required = false) |
|
|
|
private TuskClientService tuskClientService; |
|
|
|
|
|
|
|
/** |
|
|
|
* 每5秒检查机械臂拣选数据并推送 |
|
|
|
* |
|
|
|
@ -348,5 +355,281 @@ public class DashboardPushTask { |
|
|
|
return (assistArmList == null || assistArmList.isEmpty()) |
|
|
|
&& (inboundList == null || inboundList.isEmpty()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 每5秒检查智能立体仓库看板数据并推送 |
|
|
|
* |
|
|
|
* <p><b>数据来源:</b></p> |
|
|
|
* <ul> |
|
|
|
* <li>任务统计数据 - queryWarehouseTaskStats</li> |
|
|
|
* <li>库位利用率 - queryWarehouseStorageUtilization</li> |
|
|
|
* <li>机器人状态 - queryWarehouseRobotStatus</li> |
|
|
|
* <li>AGV状态 - queryWarehouseAgvStatus</li> |
|
|
|
* <li>领料申请单统计 - queryWarehouseMaterialRequestStats</li> |
|
|
|
* <li>发货统计 - queryWarehouseShipmentStats</li> |
|
|
|
* </ul> |
|
|
|
*/ |
|
|
|
@Scheduled(fixedRate = 5000) |
|
|
|
public void pushWarehouse3dBoardData() { |
|
|
|
try { |
|
|
|
// 从数据库获取立体仓库看板数据 |
|
|
|
Map<String, Object> data = getWarehouse3dBoardDataFromDb(); |
|
|
|
|
|
|
|
// 如果返回null,转换为空数据 |
|
|
|
if (data == null) { |
|
|
|
data = createEmptyWarehouse3dData(); |
|
|
|
} |
|
|
|
|
|
|
|
// 计算数据哈希值 |
|
|
|
int currentHash = data.hashCode(); |
|
|
|
int lastHash = lastDataHash.getOrDefault("warehouse-3d", 0); |
|
|
|
|
|
|
|
// 只在数据变更时推送 |
|
|
|
if (currentHash != lastHash) { |
|
|
|
boolean isEmpty = isWarehouse3dDataEmpty(data); |
|
|
|
if (isEmpty) { |
|
|
|
log.info("=== 智能立体仓库看板数据为空,推送空数据 ==="); |
|
|
|
} else { |
|
|
|
log.info("=== 检测到智能立体仓库看板数据变更,推送到前端 ==="); |
|
|
|
} |
|
|
|
webSocketService.pushWarehouse3dBoardData(data); |
|
|
|
lastDataHash.put("warehouse-3d", currentHash); |
|
|
|
} else { |
|
|
|
log.debug("智能立体仓库看板数据无变化,跳过推送"); |
|
|
|
} |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
log.error("推送智能立体仓库看板数据失败,推送空数据: {}", e.getMessage(), e); |
|
|
|
// 异常时推送空数据,避免前端显示过期数据 |
|
|
|
try { |
|
|
|
Map<String, Object> emptyData = createEmptyWarehouse3dData(); |
|
|
|
webSocketService.pushWarehouse3dBoardData(emptyData); |
|
|
|
lastDataHash.put("warehouse-3d", emptyData.hashCode()); |
|
|
|
} catch (Exception ex) { |
|
|
|
log.error("推送空数据失败: {}", ex.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 从数据库获取智能立体仓库看板数据 |
|
|
|
* |
|
|
|
* @return 智能立体仓库看板数据 |
|
|
|
*/ |
|
|
|
private Map<String, Object> getWarehouse3dBoardDataFromDb() { |
|
|
|
try { |
|
|
|
log.debug("开始从数据库获取智能立体仓库看板数据"); |
|
|
|
|
|
|
|
// 查询任务统计数据 |
|
|
|
//Map<String, Object> taskStats = dashboardDao.queryWarehouseTaskStats(); |
|
|
|
Map<String, Object> taskStats = new HashMap<>(); |
|
|
|
log.debug("任务统计数据: {}", taskStats); |
|
|
|
|
|
|
|
// 查询库位利用率数据 |
|
|
|
//Map<String, Object> storageUtilization = dashboardDao.queryWarehouseStorageUtilization(); |
|
|
|
Map<String, Object> storageUtilization = new HashMap<>(); |
|
|
|
log.debug("库位利用率数据: {}", storageUtilization); |
|
|
|
|
|
|
|
// 查询机器人状态数据 |
|
|
|
//List<Map<String, Object>> robotStatus = dashboardDao.queryWarehouseRobotStatus(); |
|
|
|
List<Map<String, Object>> robotStatus = new ArrayList<>(); |
|
|
|
log.debug("查询到机器人状态数据: {}条", robotStatus != null ? robotStatus.size() : 0); |
|
|
|
|
|
|
|
// 查询AGV状态数据(从TUSK系统获取) |
|
|
|
List<Map<String, Object>> agvStatus = getAgvStatusFromTusk(); |
|
|
|
log.debug("查询到AGV状态数据: {}条", agvStatus != null ? agvStatus.size() : 0); |
|
|
|
|
|
|
|
// 查询领料申请单统计 |
|
|
|
//Map<String, Object> materialRequestStats = dashboardDao.queryWarehouseMaterialRequestStats(); |
|
|
|
Map<String, Object> materialRequestStats = new HashMap<>(); |
|
|
|
log.debug("领料申请单统计: {}", materialRequestStats); |
|
|
|
|
|
|
|
// 查询发货统计 |
|
|
|
//Map<String, Object> shipmentStats = dashboardDao.queryWarehouseShipmentStats(); |
|
|
|
Map<String, Object> shipmentStats = new HashMap<>(); |
|
|
|
log.debug("发货统计: {}", shipmentStats); |
|
|
|
|
|
|
|
// 构造返回数据 |
|
|
|
Map<String, Object> resultData = new HashMap<>(); |
|
|
|
resultData.put("taskData", taskStats != null ? taskStats : new HashMap<>()); |
|
|
|
resultData.put("storageData", storageUtilization != null ? storageUtilization : new HashMap<>()); |
|
|
|
resultData.put("robotData", robotStatus != null ? robotStatus : new ArrayList<>()); |
|
|
|
resultData.put("agvData", agvStatus != null ? agvStatus : new ArrayList<>()); |
|
|
|
resultData.put("materialRequestData", materialRequestStats != null ? materialRequestStats : new HashMap<>()); |
|
|
|
resultData.put("shipmentData", shipmentStats != null ? shipmentStats : new HashMap<>()); |
|
|
|
|
|
|
|
log.debug("智能立体仓库看板数据组装完成"); |
|
|
|
return resultData; |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
log.error("从数据库获取智能立体仓库看板数据失败: {}", e.getMessage(), e); |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 创建空的智能立体仓库数据 |
|
|
|
* |
|
|
|
* @return 空的智能立体仓库数据结构 |
|
|
|
*/ |
|
|
|
private Map<String, Object> createEmptyWarehouse3dData() { |
|
|
|
Map<String, Object> emptyData = new HashMap<>(); |
|
|
|
emptyData.put("taskData", new HashMap<>()); |
|
|
|
emptyData.put("storageData", new HashMap<>()); |
|
|
|
emptyData.put("robotData", new ArrayList<>()); |
|
|
|
emptyData.put("agvData", new ArrayList<>()); |
|
|
|
emptyData.put("materialRequestData", new HashMap<>()); |
|
|
|
emptyData.put("shipmentData", new HashMap<>()); |
|
|
|
return emptyData; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 判断智能立体仓库数据是否为空 |
|
|
|
* |
|
|
|
* @param data 待检查的数据 |
|
|
|
* @return true=数据为空,false=数据不为空 |
|
|
|
*/ |
|
|
|
private boolean isWarehouse3dDataEmpty(Map<String, Object> data) { |
|
|
|
if (data == null || data.isEmpty()) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
Map<?, ?> taskData = (Map<?, ?>) data.get("taskData"); |
|
|
|
Map<?, ?> storageData = (Map<?, ?>) data.get("storageData"); |
|
|
|
List<?> robotData = (List<?>) data.get("robotData"); |
|
|
|
List<?> agvData = (List<?>) data.get("agvData"); |
|
|
|
|
|
|
|
return (taskData == null || taskData.isEmpty()) |
|
|
|
&& (storageData == null || storageData.isEmpty()) |
|
|
|
&& (robotData == null || robotData.isEmpty()) |
|
|
|
&& (agvData == null || agvData.isEmpty()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 从TUSK系统获取AGV状态数据 |
|
|
|
* |
|
|
|
* <p><b>数据转换说明:</b></p> |
|
|
|
* <ul> |
|
|
|
* <li>从TUSK获取原始AGV状态</li> |
|
|
|
* <li>转换为看板需要的格式</li> |
|
|
|
* <li>映射状态码为状态文本</li> |
|
|
|
* </ul> |
|
|
|
* |
|
|
|
* @return AGV状态列表 |
|
|
|
*/ |
|
|
|
private List<Map<String, Object>> getAgvStatusFromTusk() { |
|
|
|
List<Map<String, Object>> agvList = new ArrayList<>(); |
|
|
|
|
|
|
|
try { |
|
|
|
// 如果TUSK客户端服务未配置,返回空列表 |
|
|
|
if (tuskClientService == null) { |
|
|
|
log.debug("TUSK客户端服务未配置,跳过AGV状态查询"); |
|
|
|
return agvList; |
|
|
|
} |
|
|
|
|
|
|
|
// 调用TUSK接口获取在线AGV列表 |
|
|
|
TuskResponse<List<AgvStatus>> response = tuskClientService.getOnlineRobots(); |
|
|
|
|
|
|
|
if (!response.isSuccess() || response.getData() == null) { |
|
|
|
log.warn("从TUSK获取AGV状态失败: {}", response.getMsg()); |
|
|
|
return agvList; |
|
|
|
} |
|
|
|
|
|
|
|
// 转换TUSK数据为看板格式 |
|
|
|
List<AgvStatus> tuskAgvList = response.getData(); |
|
|
|
for (AgvStatus agvStatus : tuskAgvList) { |
|
|
|
Map<String, Object> agv = new HashMap<>(); |
|
|
|
|
|
|
|
// AGV编号 |
|
|
|
agv.put("id", agvStatus.getId()); |
|
|
|
agv.put("name", "AGV#" + agvStatus.getId()); |
|
|
|
|
|
|
|
// 状态转换 |
|
|
|
String status = convertAgvStatus(agvStatus.getAgvStat()); |
|
|
|
agv.put("status", status.toLowerCase()); // working/idle/charging/error |
|
|
|
agv.put("statusText", getAgvStatusText(agvStatus.getAgvStat())); |
|
|
|
|
|
|
|
// 电量 |
|
|
|
agv.put("battery", agvStatus.getSoc()); |
|
|
|
|
|
|
|
// 当前任务数(根据状态判断:运行中为1,否则为0) |
|
|
|
int tasks = (agvStatus.getAgvStat() >= 1 && agvStatus.getAgvStat() <= 12) ? 1 : 0; |
|
|
|
agv.put("tasks", tasks); |
|
|
|
|
|
|
|
agvList.add(agv); |
|
|
|
} |
|
|
|
|
|
|
|
log.debug("成功从TUSK获取{}个AGV状态", agvList.size()); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
log.error("从TUSK获取AGV状态异常: {}", e.getMessage(), e); |
|
|
|
} |
|
|
|
|
|
|
|
return agvList; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 转换AGV状态码为标准状态 |
|
|
|
* |
|
|
|
* @param agvStat TUSK系统的AGV状态码 |
|
|
|
* @return 标准状态 (working/idle/charging/error) |
|
|
|
*/ |
|
|
|
private String convertAgvStatus(Integer agvStat) { |
|
|
|
if (agvStat == null) { |
|
|
|
return "idle"; |
|
|
|
} |
|
|
|
|
|
|
|
if (agvStat == 0) { |
|
|
|
return "idle"; // 空闲 |
|
|
|
} else if (agvStat >= 1 && agvStat <= 12) { |
|
|
|
return "working"; // 运行中 |
|
|
|
} else if (agvStat == 13) { |
|
|
|
return "charging"; // 充电中 |
|
|
|
} else if (agvStat >= 128) { |
|
|
|
return "error"; // 异常状态 |
|
|
|
} |
|
|
|
|
|
|
|
return "idle"; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取AGV状态文本(中文) |
|
|
|
* |
|
|
|
* @param agvStat TUSK系统的AGV状态码 |
|
|
|
* @return 状态文本 |
|
|
|
*/ |
|
|
|
private String getAgvStatusText(Integer agvStat) { |
|
|
|
if (agvStat == null) { |
|
|
|
return "空闲"; |
|
|
|
} |
|
|
|
|
|
|
|
if (agvStat == 0) { |
|
|
|
return "空闲"; |
|
|
|
} else if (agvStat == 1) { |
|
|
|
return "运行中"; |
|
|
|
} else if (agvStat == 2) { |
|
|
|
return "直线运动中"; |
|
|
|
} else if (agvStat == 3) { |
|
|
|
return "旋转中"; |
|
|
|
} else if (agvStat == 13) { |
|
|
|
return "充电中"; |
|
|
|
} else if (agvStat == 23) { |
|
|
|
return "暂停"; |
|
|
|
} else if (agvStat == 128) { |
|
|
|
return "异常状态"; |
|
|
|
} else if (agvStat == 129) { |
|
|
|
return "急停"; |
|
|
|
} else if (agvStat == 130) { |
|
|
|
return "碰撞告警"; |
|
|
|
} else if (agvStat == 131) { |
|
|
|
return "告警"; |
|
|
|
} else if (agvStat >= 1 && agvStat <= 12) { |
|
|
|
return "运行中"; |
|
|
|
} else if (agvStat >= 128) { |
|
|
|
return "异常"; |
|
|
|
} |
|
|
|
|
|
|
|
return "未知状态"; |
|
|
|
} |
|
|
|
} |
|
|
|
|