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