3D物料信息

This commit is contained in:
cbs 2025-05-27 17:50:19 +08:00
parent 26fa0dfb97
commit bf2fdaa023
15 changed files with 319 additions and 77 deletions

View File

@ -29,9 +29,9 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.REMOTE_ROB
@Component
@Slf4j
public class RemoteControllerProcessor {
private final ConcurrentHashMap<String, RemoteControllerSocketDTO> cache = new ConcurrentHashMap<>();
public final ConcurrentHashMap<String, RemoteControllerSocketDTO> cache = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
@Value("${remote.msg}")
private String defaultMsg;
@ -185,12 +185,12 @@ public class RemoteControllerProcessor {
}
public RemoteControllerProcessor() {
/*public RemoteControllerProcessor() {
// 每秒执行一次 - 处理并发送数据 - 避免数据丢失
scheduler.scheduleAtFixedRate(this::processAndSend, 150, 150, TimeUnit.MILLISECONDS);
}
}*/
private void processAndSend() {
public void processAndSend() {
if (ObjectUtil.isEmpty(cache)) {
return;
}

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.remote.api.robot;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@EnableScheduling
@Component
@Slf4j
public class RemoteScheduledTasks {
private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
@Resource
private RemoteControllerProcessor remoteControllerProcessor;
// 这个方法将每200毫秒执行一次
@Scheduled(fixedDelay = 300, timeUnit = TimeUnit.MILLISECONDS)
public void executeTask() {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
remoteControllerProcessor.processAndSend();
}
});
}
}

View File

@ -31,4 +31,9 @@ public class RobotInformationVO {
* 车辆即将走的点位
*/
private List<String> data;
/**
* 车辆身上的物料信息和数量
*/
private Object skuInfo;
}

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.system.api.robot.dto.FloorZoneDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataErrorDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotStatusDataPoseDTO;
import cn.iocoder.yudao.module.system.api.robot.vo.RobotReactiveStatusDTO;
import cn.iocoder.yudao.module.system.constant.area.FloorAreaConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.dal.dataobject.log.RobotTaskDetailActionLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.robot.RobotWarnCodeMappingDO;
@ -64,7 +65,7 @@ public class RobotReactiveStatusApiImpl implements RobotReactiveStatusApi {
@Override
public void robotReactiveStatus(String message) {
TenantContextHolder.setTenantId(1L);
log.info("车辆所在楼层和异常信息 :{}",message);
log.info("车辆所在楼层和异常信息 :{}", message);
RobotReactiveStatusDTO data = JSON.parseObject(message, RobotReactiveStatusDTO.class);
String floorAreaKey = RobotTaskChcheConstant.ROBOT_FLOOR_AREA + data.getMac();
String robotNo = robotInformationService.getRobotNoByMac(data.getMac());
@ -73,8 +74,14 @@ public class RobotReactiveStatusApiImpl implements RobotReactiveStatusApi {
FloorZoneDTO floorZone = new FloorZoneDTO();
if (ObjectUtil.isNotEmpty(o)) {
floorZone = JSON.parseObject((String) o, FloorZoneDTO.class);
String oldFloorArea = floorZone.getFloor()+"-"+floorZone.getArea();
redisUtil.hdel(oldFloorArea,robotNo);
String oldFloorArea = floorZone.getFloor() + "-" + floorZone.getArea();
redisUtil.hdel(oldFloorArea, robotNo);
String newFloorArea = data.getFloorZone().getFloor() + "-" + data.getFloorZone().getArea();
String key = FloorAreaConstant.FLOOR_AREA_ROBOT + floorZone.getFloor() + "-" + floorZone.getArea();
if (!oldFloorArea.equals(newFloorArea)) {
redisUtil.hdel(key, robotNo);
}
}
floorZone.setArea(data.getFloorZone().getArea());
@ -84,8 +91,8 @@ public class RobotReactiveStatusApiImpl implements RobotReactiveStatusApi {
redisUtil.set(floorAreaKey, JSON.toJSONString(floorZone));
Map<String, Object> map = new HashMap<>();
String value = floorZone.getFloor() + "-" + floorZone.getArea();
map.put(robotNo,value);
redisUtil.hmset(value,map);
map.put(robotNo, value);
redisUtil.hmset(value, map);
String speedKey = RobotTaskChcheConstant.ROBOT_SPEED_FORK_HEIGHT + robotNo;
redisUtil.set(speedKey, message);
@ -158,7 +165,7 @@ public class RobotReactiveStatusApiImpl implements RobotReactiveStatusApi {
warnMsgDOS.add(warnMsg);
if (level < mappingDOS.getWarnLevel()) {
level = mappingDOS.getWarnLevel();
level = mappingDOS.getWarnLevel();
msg = warnMsg.getWarnMsg();
}
}

View File

@ -105,7 +105,7 @@ public class RobotStatusApiImpl implements RobotStatusApi {
Map<String, Object> map = new HashMap<>();
String value = FloorAreaConstant.FLOOR_AREA_ROBOT + floorZoneDTO.getFloor() + "-" + floorZoneDTO.getArea();
map.put(robotNo, robotInformationVO);
map.put(robotStatusDataDTO.getMac(), JSON.toJSONString(robotInformationVO));
redisUtil.hmset(value, map, 20);
// 合并请求 - 这里接受到的数据都丢给 RequestProcessor - 再整合数据通过WebSocket丢给前端

View File

@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.mqtt.api.common.CommonApi;
import cn.iocoder.yudao.module.system.api.path.vo.RobotClosePathPlantingDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotCommandStateDTO;
import cn.iocoder.yudao.module.system.api.robot.dto.RobotCompleteTaskDTO;
import cn.iocoder.yudao.module.system.api.robot.vo.RobotSkuInfoDTO;
import cn.iocoder.yudao.module.system.constant.CommonConstant;
import cn.iocoder.yudao.module.system.constant.path.PathPlanningTopicConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotExecutionStateConstant;
@ -204,6 +205,19 @@ public class RobotTaskStatusApiImpl implements RobotTaskStatusApi {
*/
private void robotTaskDone(RobotCompleteTaskDTO robotCompleteTaskDTO) {
setLastLogDone(robotCompleteTaskDTO.getOrderId());
//释放库位数据
if (PathTaskTypeEnum.TAKE.getType().equals(robotCompleteTaskDTO.getOrderType())
|| (PathTaskTypeEnum.TAKE_RELEASE.getType().equals(robotCompleteTaskDTO.getOrderType())
&& CommandTypeEnum.WORK_PICK_UP_GOODS.getType().equals(robotCompleteTaskDTO.getCommandStatus().getCommandType()))) {
releaseFromLocationAndSetRobotSkuInfo(robotCompleteTaskDTO);
} else if (PathTaskTypeEnum.RELEASE.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.TAKE_RELEASE.getType().equals(robotCompleteTaskDTO.getOrderType())) {
releaseToLocation(robotCompleteTaskDTO);
redisUtil.del(RobotTaskChcheConstant.ROBOT_TASK_SKU_INFO + robotCompleteTaskDTO.getMac());
} else {
redisUtil.del(RobotTaskChcheConstant.ROBOT_TASK_SKU_INFO + robotCompleteTaskDTO.getMac());
}
//todo 后面考虑下充电车机目前对充电的逻辑未定义
if (PathTaskTypeEnum.MOVE.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.MOVE_TO_WAIT_STOP.getType().equals(robotCompleteTaskDTO.getOrderType())
@ -228,7 +242,7 @@ public class RobotTaskStatusApiImpl implements RobotTaskStatusApi {
}
if (PathTaskTypeEnum.AUTO_CHARGE.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.CHARGE.getType().equals(robotCompleteTaskDTO.getOrderType())) {
|| PathTaskTypeEnum.CHARGE.getType().equals(robotCompleteTaskDTO.getOrderType())) {
RobotChargeLogDO build = RobotChargeLogDO
.builder()
.id(robotCompleteTaskDTO.getOrderId())
@ -237,6 +251,85 @@ public class RobotTaskStatusApiImpl implements RobotTaskStatusApi {
chargeLogMapper.updateById(build);
}
taskDetailActionLogMapper.updateActionStatus(robotCompleteTaskDTO.getOrderId(), ActionStatusEnum.DONE.getType(), LocalDateTime.now());
}
/**
* 释放放货库位
*
* @param robotCompleteTaskDTO
*/
private void releaseToLocation(RobotCompleteTaskDTO robotCompleteTaskDTO) {
RobotTaskDetailDO robotTaskDetailDO = robotTaskDetailMapper.selectById(robotCompleteTaskDTO.getOrderId());
if (ObjectUtil.isEmpty(robotTaskDetailDO) || ObjectUtil.isEmpty(robotTaskDetailDO.getToLocationId())) {
return;
}
WareHouseLocationDO toWareHouseLocation = locationMapper.selectById(robotTaskDetailDO.getToLocationId());
if (ObjectUtil.isEmpty(toWareHouseLocation)) {
return;
}
//查询最近一起取货任务
WareHouseLocationDO fromWareHouseLocation = getFromLocation(robotTaskDetailDO);
if (ObjectUtil.isNotEmpty(fromWareHouseLocation)) {
toWareHouseLocation.setSkuNumber(fromWareHouseLocation.getSkuNumber());
toWareHouseLocation.setSkuInfo(fromWareHouseLocation.getSkuInfo());
}
toWareHouseLocation.setLocationLock(LocationLockEnum.YES.getType());
toWareHouseLocation.setLocationUseStatus(LocationUseStatusEnum.YES.getType());
locationMapper.updateById(toWareHouseLocation);
}
/**
* 查询最近一次取货任务
*
* @param robotTaskDetailDO
* @return
*/
private WareHouseLocationDO getFromLocation(RobotTaskDetailDO robotTaskDetailDO) {
if (RobotTaskTypeEnum.TAKE_RELEASE.getType().equals(robotTaskDetailDO.getTaskType())
&& ObjectUtil.isNotEmpty(robotTaskDetailDO.getFromLocationId())) {
return locationMapper.selectById(robotTaskDetailDO.getFromLocationId());
}
List<RobotTaskDetailActionLogDO> actionLogs = taskDetailActionLogMapper.getLastTwoTask(robotTaskDetailDO.getRobotNo());
if (ObjectUtil.isEmpty(actionLogs)) {
return null;
}
for (RobotTaskDetailActionLogDO actionLog : actionLogs) {
if (!robotTaskDetailDO.getId().equals(actionLog.getTaskDetailId())) {
RobotTaskDetailDO taskDetail = robotTaskDetailMapper.selectById(actionLog.getTaskDetailId());
if (ObjectUtil.isEmpty(taskDetail) || !RobotTaskTypeEnum.TAKE.getType().equals(taskDetail.getTaskType())
|| ObjectUtil.isEmpty(taskDetail.getFromLocationId())) {
continue;
}
locationMapper.selectById(taskDetail.getFromLocationId());
}
}
return null;
}
/**
* 设置车辆上的物料信息释放取货库位
*
* @param robotCompleteTaskDTO
*/
private void releaseFromLocationAndSetRobotSkuInfo(RobotCompleteTaskDTO robotCompleteTaskDTO) {
RobotTaskDetailDO robotTaskDetailDO = robotTaskDetailMapper.selectById(robotCompleteTaskDTO.getOrderId());
if (ObjectUtil.isEmpty(robotTaskDetailDO) || ObjectUtil.isEmpty(robotTaskDetailDO.getFromLocationId())) {
return;
}
WareHouseLocationDO wareHouseLocationDO = locationMapper.selectById(robotTaskDetailDO.getFromLocationId());
if (ObjectUtil.isEmpty(wareHouseLocationDO)) {
return;
}
RobotSkuInfoDTO robotSkuInfo = new RobotSkuInfoDTO();
robotSkuInfo.setSkuNumber(wareHouseLocationDO.getSkuNumber());
robotSkuInfo.setSkuInfo(wareHouseLocationDO.getSkuInfo());
redisUtil.set(RobotTaskChcheConstant.ROBOT_TASK_SKU_INFO + robotCompleteTaskDTO.getMac(), JSON.toJSONString(robotSkuInfo));
wareHouseLocationDO.setSkuInfo(null);
wareHouseLocationDO.setSkuNumber(0L);
wareHouseLocationDO.setLocationLock(LocationLockEnum.YES.getType());
wareHouseLocationDO.setLocationUseStatus(LocationUseStatusEnum.NO.getType());
locationMapper.updateById(wareHouseLocationDO);
}
@ -445,8 +538,8 @@ public class RobotTaskStatusApiImpl implements RobotTaskStatusApi {
}
} else if (PathTaskTypeEnum.MOVE.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.MOVE_TO_WAIT.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.MOVE_TO_POINT.getType().equals(robotCompleteTaskDTO.getOrderType())) {
|| PathTaskTypeEnum.MOVE_TO_WAIT.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.MOVE_TO_POINT.getType().equals(robotCompleteTaskDTO.getOrderType())) {
if (CommandTypeEnum.MOVE_POSES.getType().equals(commandType)) {
logOne.setActionMsg("车辆正在前往" + robotTaskDetailDO.getToLocationNo());
robotTaskDetailDO.setTaskStage(RobotTaskStageEnum.MOVE.getType());
@ -479,7 +572,7 @@ public class RobotTaskStatusApiImpl implements RobotTaskStatusApi {
if (ObjectUtil.isNotEmpty(commandStatus) && CommandTypeEnum.MOVE_POSES.getType().equals(commandStatus.getCommandType())
&& RobotExecutionStateConstant.DONE.equals(commandStatus.getExecutionState())
&& (PathTaskTypeEnum.CHARGE.getType().equals(robotCompleteTaskDTO.getOrderType())
|| PathTaskTypeEnum.AUTO_CHARGE.getType().equals(robotCompleteTaskDTO.getOrderType()))) {
|| PathTaskTypeEnum.AUTO_CHARGE.getType().equals(robotCompleteTaskDTO.getOrderType()))) {
taskDetailActionLogMapper.updateActionStatus(robotCompleteTaskDTO.getOrderId(), ActionStatusEnum.DONE.getType(), LocalDateTime.now());
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.api.robot.processor;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -45,7 +46,7 @@ public class RequestProcessor {
private void sendData(String map, Map<String, String> data) {
// -- 发送给对应的websocket
// System.out.println("key:" + map + "发送数据:" + data);
// log.info("key:" + map + "发送数据:" + data);
log.info("key:" + map + "发送数据:" + data);
webSocketSenderApi.sendObject(map, "map_push", data);
}

View File

@ -1,59 +0,0 @@
/*
package cn.iocoder.yudao.module.system.api.robot.processor;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import cn.iocoder.yudao.module.system.constant.area.FloorAreaConstant;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapDO;
import cn.iocoder.yudao.module.system.service.positionmap.PositionMapService;
import cn.iocoder.yudao.module.system.util.redis.RedisUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
*/
/**
* 3D推送车辆信息
*//*
@Component
@Slf4j
public class ThreeDRequestProcessor {
private final ConcurrentHashMap<String, ConcurrentHashMap<String, String>> cache = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
@Resource
private PositionMapService positionMapService;
@Resource
private RedisUtil redisUtil;
@Resource
public WebSocketSenderApi webSocketSenderApi;
public ThreeDRequestProcessor() {
// 每秒执行一次 - 处理并发送数据 - 避免数据丢失
scheduler.scheduleAtFixedRate(this::processAndSend, 300, 300, TimeUnit.MILLISECONDS);
}
private void processAndSend() {
List<PositionMapDO> allMap = positionMapService.getAllMap();
for (PositionMapDO map : allMap) {
String key = FloorAreaConstant.FLOOR_AREA_ROBOT + map.getFloor() + "-" + map.getArea();
Map<Object, Object> data = redisUtil.hmget(key);
System.out.println("3D发送数据 "+ JSON.toJSONString(data));
webSocketSenderApi.sendObject(key, "3d_map_robot", data);
}
}
}
*/

View File

@ -0,0 +1,118 @@
package cn.iocoder.yudao.module.system.api.robot.schedul;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi;
import cn.iocoder.yudao.module.system.api.robot.vo.RobotInformationVO;
import cn.iocoder.yudao.module.system.constant.area.FloorAreaConstant;
import cn.iocoder.yudao.module.system.constant.robot.RobotTaskChcheConstant;
import cn.iocoder.yudao.module.system.controller.admin.robot.task.TaskAssignDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.positionmap.PositionMapDO;
import cn.iocoder.yudao.module.system.service.positionmap.PositionMapService;
import cn.iocoder.yudao.module.system.util.redis.RedisUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@EnableScheduling
@Component
@Slf4j
public class ScheduledTasks {
@Resource
private PositionMapService positionMapService;
@Resource
private RedisUtil redisUtil;
@Resource
public WebSocketSenderApi webSocketSenderApi;
private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 这个方法将每200毫秒执行一次
@Scheduled(fixedDelay = 300, timeUnit = TimeUnit.MILLISECONDS)
public void executeTask() {
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
sendData();
}
});
}
public void sendData() {
TenantContextHolder.setTenantId(1L);
Object o = redisUtil.get(FloorAreaConstant.FLOOR_AREA_ALL);
List<PositionMapDO> allMap = null;
if (ObjectUtil.isEmpty(o)) {
allMap = positionMapService.getAllMap();
if (ObjectUtil.isEmpty(allMap)) {
return;
}
redisUtil.set(FloorAreaConstant.FLOOR_AREA_ALL,JSON.toJSONString(allMap),60);
}else {
allMap = JSON.parseArray(String.valueOf(o), PositionMapDO.class);
}
for (PositionMapDO positionMap : allMap) {
String key = FloorAreaConstant.FLOOR_AREA_ROBOT + positionMap.getFloor() + "-" + positionMap.getArea();
Map<Object, Object> data = redisUtil.hmget(key);
if (ObjectUtil.isEmpty(data)) {
continue;
}
Map<Object, Object> map = new HashMap<>();
data.forEach((k,v) ->{
Object skuInfo = redisUtil.get(RobotTaskChcheConstant.ROBOT_TASK_SKU_INFO + k);
if (ObjectUtil.isNotEmpty(skuInfo)) {
RobotInformationVO informationVO = JSON.parseObject(v.toString(), RobotInformationVO.class);
informationVO.setSkuInfo(skuInfo);
map.put(k,informationVO);
}else {
map.put(k,v);
}
});
// log.info("3D发送数据:{}", JSON.toJSONString(map));
webSocketSenderApi.sendObject(key, "3d_map_push", map);
}
}
}

View File

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.api.robot.vo;
import lombok.Data;
@Data
public class RobotSkuInfoDTO {
/**
* 物料信息
*/
private String skuInfo;
/**
* 物料数量
*/
private Long skuNumber;
}

View File

@ -6,7 +6,8 @@ public class FloorAreaConstant {
//仅限给WEBSOCKET推送给前端使用缓存15秒
public static String FLOOR_AREA_ROBOT = "floor:area:robot";
//所有的楼层区域
public static String FLOOR_AREA_ALL = "floor:area:all";
}

View File

@ -46,4 +46,7 @@ public class RobotTaskChcheConstant {
//机器人开始空闲的时间 (拼接的是车辆编号robotNo)
public static String ROBOT_LAST_FREE_TIME = "robot:last:free:time";
//机器人物料数量(拼接的是mac地址)
public static String ROBOT_TASK_SKU_INFO = "robot:task:sku:info";
}

View File

@ -54,4 +54,11 @@ public interface RobotTaskDetailActionLogMapper extends BaseMapperX<RobotTaskDet
* @return
*/
List<RobotTaskDetailActionLogDO> getUnStatisticsDurationLog();
/**
* 查询机器人最后两条任务
* @param robotNo
* @return
*/
List<RobotTaskDetailActionLogDO> getLastTwoTask(@Param("robotNo") String robotNo);
}

View File

@ -547,6 +547,7 @@ public class RobotTaskServiceImpl extends ServiceImpl<RobotTaskMapper, RobotTask
String mac = robotInformationService.getMacByRobotNo(taskAssignDTO.getRobotNo());
String robotDoingActionKey = RobotTaskChcheConstant.ROBOT_QUERY_DOING_ACTION + mac;
redisUtil.del(robotDoingActionKey);
redisUtil.del(RobotTaskChcheConstant.ROBOT_TASK_SKU_INFO + mac);
Map<Long, String> deviceNoMap = new HashMap<>();
Integer robotStatus = RobotStatusEnum.DOING.getType();

View File

@ -50,4 +50,16 @@
and already_counted = '0'
and create_time >= DATE_SUB(NOW(), INTERVAL 48 HOUR)
</select>
<select id="getLastTwoTask"
resultType="cn.iocoder.yudao.module.system.dal.dataobject.log.RobotTaskDetailActionLogDO">
select
*
from
robot_task_detail_action_log
where
command_id = '-1'
and deleted = '0'
and robot_no = #{robotNo}
order by create_time desc
</select>
</mapper>