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;
/**
* [类的简要说明]
*
* [详细描述,可选]
*
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
@Slf4j
public class Room implements AutoCloseable {
private AtomicBoolean status = new AtomicBoolean(false);
//资源最终坐标
private Map resourceCoordinateMap = new ConcurrentHashMap<>();
//资源路线path
private Map resourcePathMap = new ConcurrentHashMap<>();
private Map 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> actionMap = new ConcurrentSkipListMap<>();
//日期格式化
private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//房间中关联的任务管理器
private Map 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> 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 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 getPathMap() {
return resourcePathMap;
}
public Map getScenarioResourceMap() {
return scenarioResourceMap;
}
private void pushRoomInfo(){
ResponseCmdInfo