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 respObj = new ResponseCmdInfo<>(); Map 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=========================="); } } }