simulation-backend/src/main/java/com/hivekion/room/bean/Room.java
2025-09-22 10:30:05 +08:00

325 lines
9.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.hivekion.room.bean;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.hivekion.Global;
import com.hivekion.baseData.entity.Scenario;
import com.hivekion.common.entity.ResponseCmdInfo;
import com.hivekion.common.redis.RedisUtil;
import com.hivekion.common.utils;
import com.hivekion.common.uuid.IdUtils;
import com.hivekion.room.func.TaskAction;
import com.hivekion.scenario.bean.ScenarioWsParam;
import com.hivekion.scenario.entity.ScenarioResource;
import com.hivekion.scenario.service.impl.ScenarioResourceServiceImpl;
import com.hivekion.statistic.bean.EditScenarioInfo;
import com.hivekion.statistic.bean.ScenarioInfo;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* [类的简要说明]
* <p>
* [详细描述,可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
@Slf4j
public class Room implements AutoCloseable {
private AtomicBoolean status = new AtomicBoolean(false);
//资源最终坐标
private Map<String, Coordinate> resourceCoordinateMap = new ConcurrentHashMap<>();
//资源路线path
private Map<String, Object> resourcePathMap = new ConcurrentHashMap<>();
private Map<String,ScenarioResource> scenarioResourceMap = new ConcurrentHashMap<>();
/**
* 任务管理相关
*/
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> future;
/**
* 房间ID
*/
private String roomId;
/**
* 想定信息
*/
private Scenario scenario;
private RedisUtil redisUtil;
private com.hivekion.statistic.service.ScenarioService scenarioService;
private AtomicInteger numStatus = new AtomicInteger(0);
/**
* 任务容器
*/
private NavigableMap<Long, Map<String, TaskAction>> actionMap = new ConcurrentSkipListMap<>();
//日期格式化
private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//房间中关联的任务管理器
private Map<String, ScheduledExecutorService> futures = new ConcurrentHashMap<>();
//线程池
private final ExecutorService actionExecutor =
new ThreadPoolExecutor(
5, 100, // corePoolSize, maximumPoolSize
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(1000), // 有界队列只允许100个待执行任务
new ThreadPoolExecutor.AbortPolicy() // 超出直接抛异常
);
public Room(String roomId, Scenario scenario) {
this.roomId = roomId;
this.scenario = scenario;
}
/**
* 持续时间
*/
private AtomicLong duringTime = new AtomicLong(0);
private AtomicLong totalTime = new AtomicLong(0);
private int mag = 1;
/**
* 启动
*
* @param time 总时间
*/
public void start(long time) {
status.set(true);
totalTime.set(time);
startTask();
//初始化系统资源 物资人员等信息
initRoomParam();
pushRoomInfo();
numStatus.set(1);
}
/**
* 停止
*/
public void stop() {
numStatus.set(3);
status.set(false);
pushRoomInfo();
cancelTask();
futures.forEach((key, value) -> {
try {
if (!value.isShutdown()) {
value.shutdownNow();
}
} catch (Exception e) {
log.error("error::", e);
}
});
//清除房间缓存数据
this.clearScenarioCache();
}
/**
* 暂停
*/
public void pause() {
numStatus.set(2);
status.set(false);
pushRoomInfo();
cancelTask();
}
public void resume() {
status.set(true);
pushRoomInfo();
startTask();
}
public long getDuringTime() {
return duringTime.get();
}
// 启动定时任务
private void startTask() {
if (future == null || future.isCancelled()) {
future = scheduler.scheduleAtFixedRate(() -> {
ScenarioWsParam magValue = Global.roomParamMap.get(
this.scenario.getId() + "_" + this.roomId);
if (magValue != null) {
this.mag = magValue.getMag();
}
long curTime = duringTime.addAndGet(this.mag);
sendRemainTime((totalTime.get() - curTime));
NavigableMap<Long, Map<String, TaskAction>> actions = actionMap.headMap(curTime, true);
if (!actions.isEmpty()) {
actions.forEach((key, action) -> {
action.forEach((taskAction, task) -> {
actionExecutor.submit(task::doSomeThing);
});
});
actions.clear();
}
}, 0, 1, TimeUnit.SECONDS);
}
}
// 取消定时任务
private void cancelTask() {
if (future != null && !future.isCancelled()) {
future.cancel(true);
}
}
public void addAction(long time, TaskAction action) {
actionMap.computeIfAbsent(time, k -> new ConcurrentHashMap<>())
.put(IdUtils.simpleUUID(), action);
}
@Override
public void close() throws Exception {
actionMap.clear();
if (future != null && !future.isCancelled()) {
future.cancel(true);
}
if (scheduler != null && !scheduler.isShutdown()) {
scheduler.shutdown();
}
}
private void sendRemainTime(long remainTime) {
Map<String, Object> timeMap = new HashMap<>();
timeMap.put("update_time_str", utils.formatSeconds(remainTime));
timeMap.put("remain_time", remainTime);
timeMap.put("during_time", duringTime.get());
timeMap.put("current_time",
df.format(this.scenario.getStartTime().plusSeconds(duringTime.get())));
Global.sendCmdInfoQueue.add(
ResponseCmdInfo.create("update_time", this.roomId, this.scenario.getId(),
timeMap));
}
public void addTaskReference(ScheduledExecutorService scheduledExecutorService) {
futures.put(IdUtils.simpleUUID(), scheduledExecutorService);
}
public boolean isRunning() {
return status.get();
}
public void initRoomParam() {
long begTime = System.currentTimeMillis();
if (scenarioService == null) {
scenarioService = SpringUtil.getBean(com.hivekion.statistic.service.ScenarioService.class);
}
//设置资源列表
scenario.setResourceList(SpringUtil.getBean(ScenarioResourceServiceImpl.class)
.getResourceListByScenarioId(scenario.getId()));
for (ScenarioResource scenarioResource : this.scenario.getResourceList()) {
ScenarioInfo scenarioInfo = scenarioService.listScenarioInfo(scenarioResource.getScenarioId(),
roomId, scenarioResource.getId());
EditScenarioInfo updScenarioInfo = scenarioService.listEditScenarioInfo(
scenarioResource.getScenarioId(), roomId, scenarioResource.getId());
if (redisUtil == null) {
redisUtil = SpringUtil.getBean(RedisUtil.class);
}
redisUtil.hset(
scenarioResource.getScenarioId() + "-" + roomId + "-" + scenarioResource.getId(),
"scenarioInfo", JSON.toJSONString(scenarioInfo));
redisUtil.hset(
scenarioResource.getScenarioId() + "-" + roomId + "-" + scenarioResource.getId(),
"updScenarioInfo", JSON.toJSONString(updScenarioInfo));
}
scenario.getResourceList().forEach(resource -> {
scenarioResourceMap.put(resource.getId(), resource);
});
long endTime = System.currentTimeMillis();
log.info("======cost time============={}===================",endTime-begTime);
}
public void addResourcePath(String resourceId, Object obj) {
resourcePathMap.put(resourceId, obj);
}
public Object getResourcePath(String resourceId) {
return resourcePathMap.get(resourceId);
}
public void addResourceLastPosition(String resourceId, Coordinate obj) {
resourceCoordinateMap.put(resourceId, obj);
}
public Coordinate getResourceLastPosition(String resourceId) {
return resourceCoordinateMap.get(resourceId);
}
public Map<String, Object> getPathMap() {
return resourcePathMap;
}
public Map<String, ScenarioResource> getScenarioResourceMap() {
return scenarioResourceMap;
}
private void pushRoomInfo(){
ResponseCmdInfo<Object> respObj = new ResponseCmdInfo<>();
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("mag", this.getMag());
dataMap.put("status", this.numStatus.get());
respObj.setData(dataMap);
respObj.setRoom(this.getRoomId());
respObj.setScenarioId(this.getScenario().getId());
respObj.setCmdType("room_info");
Global.sendCmdInfoQueue.add(respObj);
}
private void clearScenarioCache(){
try {
scenario.setResourceList(SpringUtil.getBean(ScenarioResourceServiceImpl.class)
.getResourceListByScenarioId(scenario.getId()));
if (redisUtil == null) {
redisUtil = SpringUtil.getBean(RedisUtil.class);
}
for (ScenarioResource scenarioResource : this.scenario.getResourceList()) {
ScenarioInfo scenarioInfo = scenarioService.listScenarioInfo(scenarioResource.getScenarioId(),
roomId, scenarioResource.getId());
EditScenarioInfo updScenarioInfo = scenarioService.listEditScenarioInfo(
scenarioResource.getScenarioId(), roomId, scenarioResource.getId());
redisUtil.delete(
scenarioResource.getScenarioId() + "-" + roomId + "-" + scenarioResource.getId());
redisUtil.delete(
scenarioResource.getScenarioId() + "-" + roomId + "-" + scenarioResource.getId());
}
}catch (Exception ex){
log.error("===================clear cached fail==========================");
}
}
}