任务相关

This commit is contained in:
李玉东 2025-09-18 10:47:37 +08:00
parent 720748ea1e
commit b6bc87ffab
15 changed files with 492 additions and 53 deletions

View File

@ -19,13 +19,13 @@ public class ResponseCmdInfo<T> implements java.io.Serializable {
private Integer scenarioId;
private T data;
@Override
public String toString() {
return "ResponseCmdInfo{" +
"cmdType='" + cmdType + '\'' +
", room='" + room + '\'' +
", scenarioId=" + scenarioId +
", data=" + data +
'}';
public static <T> ResponseCmdInfo<T> create(String type, String room, Integer scenarioId,
T data) {
ResponseCmdInfo<T> responseCmdInfo = new ResponseCmdInfo<T>();
responseCmdInfo.setCmdType(type);
responseCmdInfo.setRoom(room);
responseCmdInfo.setScenarioId(scenarioId);
responseCmdInfo.setData(data);
return responseCmdInfo;
}
}

View File

@ -1,6 +1,8 @@
package com.hivekion.common;
import com.hivekion.common.exception.BusinessException;
import com.sun.javafx.binding.StringFormatter;
import java.time.Duration;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
@ -87,4 +89,16 @@ public class utils {
}
return level;
}
public static String formatSeconds(long totalSeconds) {
Duration duration = Duration.ofSeconds(totalSeconds);
long hours = duration.toHours();
long minutes = duration.minusHours(hours).toMinutes();
long seconds = duration.minusHours(hours).minusMinutes(minutes).getSeconds();
return StringFormatter.format("%d小时%d分钟%d秒\n", hours, minutes, seconds).get();
}
public static void main(String[] args) {
System.out.println(utils.formatSeconds(3601));
}
}

View File

@ -1,5 +1,6 @@
package com.hivekion.room;
import com.hivekion.baseData.entity.Scenario;
import com.hivekion.room.bean.Room;
import com.hivekion.room.func.TaskAction;
import java.util.Map;
@ -18,8 +19,8 @@ public class RoomManager {
private static final Map<String, Room> roomsMap = new ConcurrentHashMap<>();
public static void startRoom(String id, long time) {
Room room = new Room(id);
public static void startRoom(String id, Scenario scenario,long time) {
Room room = new Room(id,scenario);
roomsMap.put(id, room);
room.start(time);
}

View File

@ -0,0 +1,114 @@
package com.hivekion.room.bean;
import com.hivekion.Global;
import com.hivekion.room.func.TaskAction;
import com.hivekion.scenario.bean.ScenarioWsParam;
import com.hivekion.scenario.entity.ScenarioTask;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.reactive.function.client.WebClient;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
public abstract class AbtParentTask implements TaskAction {
protected final AtomicLong duringTime = new AtomicLong(0);
protected final ScheduledExecutorService schedule = Executors.newScheduledThreadPool(
1);
protected ScheduledFuture<?> scheduledFuture;
//任务数据
protected final ScenarioTask scenarioTask;
protected final String roomId;
protected WebClient webClient = WebClient.create();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 任务队列
new CustomThreadFactory("MyPool"), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
public AbtParentTask(ScenarioTask scenarioTask, String roomId) {
this.scenarioTask = scenarioTask;
this.roomId = roomId;
}
protected void start() {
scheduledFuture = schedule.scheduleAtFixedRate(() -> {
ScenarioWsParam scenarioWsParam = Global.roomParamMap.get(
scenarioTask.getScenarioId() + "_" + roomId);
if (scenarioWsParam == null) {
duringTime.getAndSet(1);
} else {
duringTime.getAndSet(scenarioWsParam.getMag());
}
business();
}, 0, 1, TimeUnit.SECONDS);
}
protected abstract void finished();
protected abstract void setMag(int mag);
@Override
public void doSomeThing() {
}
@Override
public String getId() {
return scenarioTask.getId();
}
@Override
public String getType() {
return scenarioTask.getTaskType();
}
public void cancelAllTask() {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
}
protected abstract void business();
}
// 自定义线程工厂
class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public CustomThreadFactory(String namePrefix) {
this.namePrefix = namePrefix + "-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, namePrefix + threadNumber.getAndIncrement());
thread.setDaemon(false); // 设置为非守护线程
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
}

View File

@ -0,0 +1,41 @@
package com.hivekion.room.bean;
import com.hivekion.scenario.entity.ScenarioTask;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
public class BattleRootTask extends AbtParentTask {
public BattleRootTask(ScenarioTask scenarioTask,String roomId) {
super(scenarioTask,roomId);
}
@Override
public void doSomeThing() {
}
@Override
protected void business() {
}
@Override
protected void finished() {
}
@Override
protected void setMag(int mag) {
}
}

View File

@ -0,0 +1,104 @@
package com.hivekion.room.bean;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.hivekion.Global;
import com.hivekion.common.MultiPointGeoPosition;
import com.hivekion.common.entity.ResponseCmdInfo;
import com.hivekion.room.func.TaskAction;
import com.hivekion.scenario.entity.ScenarioTask;
import java.util.Map;
import java.util.TreeMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
@Slf4j
public class MoveRootTask extends AbtParentTask implements TaskAction {
private final double SPEED = 170;
private double accumulatedDistance = 0;
public MoveRootTask(ScenarioTask scenarioTask, String roomId) {
super(scenarioTask, roomId);
}
private final Map<Double, String> distanceInfoMap = new TreeMap<Double, String>();
@Override
public void doSomeThing() {
log.info("move task running");
//累计距离
String url = SpringUtil.getBean(Environment.class).getProperty("path.planning.url");
String params = url + "?"
+ "profile=car"
+ "&point=" + scenarioTask.getFromLat() + ","
+ scenarioTask.getFromLng()
+ "&point=" + scenarioTask.getToLat() + ","
+ scenarioTask.getToLng()
+ "points_encoded=false"
+ "&algorithm=alternative_route&alternative_route.max_paths=3";
String result = webClient.get().uri(params)
.retrieve()
.bodyToMono(String.class)
.block();
JSONObject pointJson = JSON.parseObject(result);
//获取路径点
if (pointJson != null) {
JSONObject pointsObj = pointJson.getJSONArray("paths").getJSONObject(0)
.getJSONObject("points");
//推送路径任务
Global.sendCmdInfoQueue.add(
ResponseCmdInfo.create("path_init", roomId, scenarioTask.getScenarioId(), pointsObj));
JSONArray coordinates = pointsObj.getJSONArray("coordinates");
Double beforeLng = null;
Double beforeLat = null;
for (int i = 0; i < coordinates.size(); i++) {
JSONArray coordinate = coordinates.getJSONArray(i);
Double lng = coordinate.getDouble(0);
Double lat = coordinate.getDouble(1);
if (beforeLng == null && beforeLat == null) {
distanceInfoMap.put((double) 0, lng + "," + lat);
} else {
double distance = MultiPointGeoPosition.haversine(beforeLat, beforeLng, lng, lat);
distanceInfoMap.put(distance, lng + "," + lat);
}
beforeLng = lng;
beforeLat = lat;
}
}
}
@Override
protected void business() {
}
@Override
protected void finished() {
}
@Override
protected void setMag(int mag) {
}
}

View File

@ -1,9 +1,18 @@
package com.hivekion.room.bean;
import com.hivekion.Global;
import com.hivekion.baseData.entity.Scenario;
import com.hivekion.common.entity.ResponseCmdInfo;
import com.hivekion.common.utils;
import com.hivekion.common.uuid.IdUtils;
import com.hivekion.room.func.TaskAction;
import com.hivekion.scenario.bean.ScenarioWsParam;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
@ -13,6 +22,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* [类的简要说明]
@ -24,14 +34,28 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
@Slf4j
public class Room implements AutoCloseable {
/**
* 任务管理相关
*/
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> future;
/**
* 房间ID
*/
private String roomId;
private Map<Long, Map<String, TaskAction>> actionMap = new ConcurrentHashMap<>();
/**
* 想定信息
*/
private Scenario scenario;
/**
* 任务容器
*/
private Map<Long, Map<String, TaskAction>> actionMap = new ConcurrentSkipListMap<>();
//日期格式化
private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//线程池
private final ExecutorService actionExecutor =
new ThreadPoolExecutor(
@ -41,12 +65,13 @@ public class Room implements AutoCloseable {
new ThreadPoolExecutor.AbortPolicy() // 超出直接抛异常
);
public Room(String roomId) {
public Room(String roomId, Scenario scenario) {
this.roomId = roomId;
this.scenario = scenario;
}
/**
* 剩余时间
* 持续时间
*/
private AtomicLong duringTime = new AtomicLong(0);
private AtomicLong totalTime = new AtomicLong(0);
@ -54,10 +79,6 @@ public class Room implements AutoCloseable {
private int mag = 1;
//获取剩余时间
public long getDuringTime() {
return duringTime.get();
}
/**
* 启动
@ -83,23 +104,26 @@ public class Room implements AutoCloseable {
public void pause() {
cancelTask();
}
public void resume() {
startTask();
}
/**
* 快进
*
* @param mag 放大倍数
*/
public void magChange(int mag) {
this.mag = mag;
}
// 启动定时任务
private void startTask() {
if (future == null || future.isCancelled()) {
future = scheduler.scheduleAtFixedRate(() -> {
long curTime = duringTime.addAndGet(this.mag); // 推荐用 addAndGet
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));
Map<String, TaskAction> actions = actionMap.get(curTime);
if (actions != null && !actions.isEmpty()) {
// 先复制key避免并发删除问题
@ -126,6 +150,7 @@ public class Room implements AutoCloseable {
public void addAction(long time, TaskAction action) {
actionMap.computeIfAbsent(time, k -> new ConcurrentHashMap<>())
.put(IdUtils.simpleUUID(), action);
}
@Override
@ -138,4 +163,17 @@ public class Room implements AutoCloseable {
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));
}
}

View File

@ -0,0 +1,41 @@
package com.hivekion.room.bean;
import com.hivekion.room.func.TaskAction;
import com.hivekion.scenario.entity.ScenarioTask;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
public class SupplierTask extends AbtParentTask implements TaskAction {
public SupplierTask(ScenarioTask scenarioTask,String roomId) {
super(scenarioTask,roomId);
}
@Override
protected void finished() {
}
@Override
public void doSomeThing() {
}
@Override
protected void business() {
}
@Override
protected void setMag(int mag) {
}
}

View File

@ -9,8 +9,9 @@ package com.hivekion.room.func;
* @author LiDongYU
* @since 2025/7/22
*/
@FunctionalInterface
public interface TaskAction {
void doSomeThing();
String getId();
String getType();
}

View File

@ -13,6 +13,11 @@ import lombok.Data;
*/
@Data
public class ScenarioWsParam {
//放大倍数
private Integer mag;
public ScenarioWsParam(Integer mag) {
this.mag = mag;
}
}

View File

@ -21,6 +21,8 @@ import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
@ -40,6 +42,8 @@ import org.springframework.web.bind.annotation.RestController;
*/
@RestController
@RequestMapping("/scenario/room")
@Data
@Slf4j
public class ScenarioRoomController extends BaseController {
@ -138,12 +142,15 @@ public class ScenarioRoomController extends BaseController {
Global.roomLogQueue.add(RoomLog.createRoomLog(room.getId(), "想定加速/减速",
SecurityUtils.getCurrentLoginUser().getUsername()));
if (Global.roomParamMap.get(room.getScenarioId() + "_" + room.getId()) == null) {
Global.roomParamMap.put(room.getScenarioId() + "_" + room.getId(), new ScenarioWsParam());
}
ScenarioWsParam magValue = Global.roomParamMap.get(room.getScenarioId() + "_" + room.getId());
magValue.setMag(room.getMag());
}
Global.roomParamMap.put(room.getScenarioId() + "_" + room.getId(), new ScenarioWsParam(room.getMag()));
}else{
ScenarioWsParam magValue = Global.roomParamMap.get(room.getScenarioId() + "_" + room.getId());
magValue.setMag(room.getMag());
}
log.info("magValue:{}",Global.roomParamMap.get(room.getScenarioId() + "_" + room.getId()));
}
return ResponseData.success(null);
}
@ -166,13 +173,13 @@ public class ScenarioRoomController extends BaseController {
room.setScenario(scenario);
room.setScenarioName(scenario.getName());
room.setRoomLogs(roomLogService.roomLogListLimit10(id));
if (Global.roomParamMap.get(id) == null) {
ScenarioWsParam scenarioWsParam = new ScenarioWsParam();
if (Global.roomParamMap.get(scenario.getId()+"_"+id) == null) {
ScenarioWsParam scenarioWsParam = new ScenarioWsParam(1);
scenarioWsParam.setMag(1);
Global.roomParamMap.put(id, scenarioWsParam);
Global.roomParamMap.put(scenario.getId()+"_"+id, scenarioWsParam);
room.setMag(1);
} else {
room.setMag(Global.roomParamMap.get(id).getMag());
room.setMag(Global.roomParamMap.get(scenario.getId()+"_"+id).getMag());
}
}
return ResponseData.success(room);

View File

@ -58,5 +58,11 @@ public class ScenarioTask implements Serializable {
private String taskType;
@TableField(exist = false)
private String status = "init";
@TableField(value = "during_time")
private Integer duringTime;
@TableField(value = "supplier_resource_id")
private String supplierResourceId;
@TableField(value = "supplier_num")
private double supplierNum;
}

View File

@ -9,6 +9,10 @@ import com.hivekion.baseData.service.IWeatherResourceService;
import com.hivekion.baseData.service.ScenarioService;
import com.hivekion.common.entity.ResponseCmdInfo;
import com.hivekion.room.RoomManager;
import com.hivekion.room.bean.BattleRootTask;
import com.hivekion.room.bean.MoveRootTask;
import com.hivekion.room.bean.SupplierTask;
import com.hivekion.room.func.TaskAction;
import com.hivekion.scenario.entity.ScenarioTask;
import com.hivekion.scenario.mapper.ScenarioTaskMapper;
import com.hivekion.scenario.service.ScenarioTaskService;
@ -46,9 +50,9 @@ public class ScenarioTaskServiceImpl extends
//查询想定的持续时间
Scenario scenario = scenarioService.getScenarioById(scenarioId);
if (scenario != null) {
long duringTime = Duration.between(scenario.getStartTime(), scenario.getEndTime())
long duringTime = Duration.between(scenario.getStartTime(),scenario.getEndTime() )
.getSeconds();
RoomManager.startRoom(roomId, duringTime);
RoomManager.startRoom(roomId, scenario, duringTime);
addWeatherEvent(scenario, roomId);
addTaskEvent(scenario, roomId);
}
@ -70,7 +74,6 @@ public class ScenarioTaskServiceImpl extends
}
@Override
public List<ScenarioTask> queryTaskList(ScenarioTask task) {
QueryWrapper<ScenarioTask> queryWrapper = new QueryWrapper<>();
@ -86,30 +89,94 @@ public class ScenarioTaskServiceImpl extends
*/
private void addWeatherEvent(Scenario scenario, String roomId) {
List<WeatherResource> weatherList = weatherResourceService.list();
for (WeatherResource weatherResource : weatherList) {
long diff = Duration.between(weatherResource.getLastBegTime(), scenario.getStartTime())
.getSeconds();
//开始
RoomManager.addAction(roomId, diff, () -> {
TaskAction startAction = new TaskAction() {
@Override
public void doSomeThing() {
Global.sendCmdInfoQueue.add(
create("start_" + weatherResource.getWeatherType(), scenario, roomId));
}
Global.sendCmdInfoQueue.add(
create("start_" + weatherResource.getWeatherType(), scenario, roomId));
});
@Override
public String getId() {
return "";
}
@Override
public String getType() {
return "";
}
};
RoomManager.addAction(roomId, diff, startAction);
//结束
long duringTime = Duration.between(weatherResource.getLastBegTime(),
weatherResource.getLastEndTime())
.getSeconds();
RoomManager.addAction(roomId, diff + duringTime, () -> {
Global.sendCmdInfoQueue.add(
create("stop_" + weatherResource.getWeatherType(), scenario, roomId));
});
//开始
TaskAction endAction = new TaskAction() {
@Override
public void doSomeThing() {
Global.sendCmdInfoQueue.add(
create("stop_" + weatherResource.getWeatherType(), scenario, roomId));
}
@Override
public String getId() {
return "";
}
@Override
public String getType() {
return "";
}
};
RoomManager.addAction(roomId, diff + duringTime, endAction);
}
}
//增加任务
private void addTaskEvent(Scenario scenario, String roomId) {
ScenarioTask scenarioTask = new ScenarioTask();
scenarioTask.setScenarioId(scenario.getId());
List<ScenarioTask> taskList = this.queryTaskList(scenarioTask);
for (ScenarioTask task : taskList) {
try {
long diff = Duration.between(scenario.getStartTime(),task.getStartTime())
.getSeconds();
log.info("diff::{},taskType::{}",diff,task.getTaskType());
switch (task.getTaskType()) {
//移动任务
case "1":
MoveRootTask moveRootTask = new MoveRootTask(task, roomId);
RoomManager.addAction(roomId, diff, moveRootTask);
break;
//战斗任务
case "2":
BattleRootTask battleRootTask = new BattleRootTask(task, roomId);
RoomManager.addAction(roomId, diff, battleRootTask);
break;
//补充保障任务
case "4":
case "5":
case "6":
case "7":
case "8":
SupplierTask supplierTask = new SupplierTask(task, roomId);
RoomManager.addAction(roomId, diff, supplierTask);
break;
default:
break;
}
} catch (Exception e) {
log.error("error::", e);
}
}
}
private ResponseCmdInfo<Void> create(String type, Scenario scenario, String roomId) {

View File

@ -111,11 +111,11 @@ public class WsServer {
}
public static void sendMessage(Integer scenarioId, String room, String message) {
log.info("send {},{},{}", message, scenarioId, room);
// log.info("send {},{},{}", message, scenarioId, room);
Object lock = lockMap.computeIfAbsent(scenarioId, k -> new Object());
synchronized (lock) {
Map<String, Map<String, Session>> roomMap = SESSION_MAP.get(String.valueOf(scenarioId));
log.info("roomMap:{}", roomMap);
// log.info("roomMap:{}", roomMap);
if (roomMap != null) {
if (roomMap.containsKey(room)) {
Map<String, Session> singleRoomMap = roomMap.get(room);

View File

@ -27,7 +27,7 @@ spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
mybatis-plus.configuration.map-underscore-to-camel-case=false
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.configuration.database-id=dm
user.default.password=1q2w3E*
user.default.failsCount=5