diff --git a/src/main/demo.html b/src/main/demo.html index 4552271..d4dcd15 100644 --- a/src/main/demo.html +++ b/src/main/demo.html @@ -1,89 +1,86 @@ - + - - Google Map 绘制路线 - + + WS实时推送轨迹 - Google Map +
- + + \ No newline at end of file diff --git a/src/main/java/com/hivekion/DemoApplication.java b/src/main/java/com/hivekion/DemoApplication.java index fe9156f..47ad40b 100644 --- a/src/main/java/com/hivekion/DemoApplication.java +++ b/src/main/java/com/hivekion/DemoApplication.java @@ -1,5 +1,6 @@ package com.hivekion; +import cn.hutool.extra.spring.EnableSpringUtil; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,8 +14,9 @@ import java.time.LocalDate; import java.time.LocalTime; @SpringBootApplication +@EnableSpringUtil @MapperScan({"com.hivekion.**.mapper"}) -@ComponentScan(basePackages = { "com.hivekion.*"}) +@ComponentScan(basePackages = {"cn.hutool.extra.spring","com.hivekion.*"}) public class DemoApplication extends SpringBootServletInitializer { private static Logger log = LoggerFactory.getLogger(DemoApplication.class); diff --git a/src/main/java/com/hivekion/Test.java b/src/main/java/com/hivekion/Test.java index e990746..3dfaf84 100644 --- a/src/main/java/com/hivekion/Test.java +++ b/src/main/java/com/hivekion/Test.java @@ -1,5 +1,8 @@ package com.hivekion; +import java.util.NavigableMap; +import java.util.TreeMap; + /** * [类的简要说明] *

@@ -12,13 +15,12 @@ package com.hivekion; public class Test { public static void main(String[] args) { - String test = "id\n" - + "general_time\n" - + "from_resource_id\n" - + "supplier_type\n" - + "supplier_num\n" - + "lat\n" - + "lng"; - System.out.println(test.toUpperCase()); + TreeMap treeMap = new TreeMap(); + treeMap.put(new Double("1"), "1"); + treeMap.put(new Double("5"), "1"); + treeMap.put(new Double("9"), "1"); + treeMap.put(new Double("20"), "1"); + treeMap.put(new Double("21"), "1"); + System.out.println(treeMap.ceilingKey(new Double("22"))); ; } } diff --git a/src/main/java/com/hivekion/common/MultiPointGeoPosition.java b/src/main/java/com/hivekion/common/MultiPointGeoPosition.java index de99522..3ab572d 100644 --- a/src/main/java/com/hivekion/common/MultiPointGeoPosition.java +++ b/src/main/java/com/hivekion/common/MultiPointGeoPosition.java @@ -40,6 +40,7 @@ public class MultiPointGeoPosition { } public static double[] interpolate(double lat1, double lon1, double lat2, double lon2, double f) { + lat1 = toRadians(lat1); lon1 = toRadians(lon1); lat2 = toRadians(lat2); @@ -118,4 +119,21 @@ public class MultiPointGeoPosition { PositionResult pos = getPosition(points, speed, time); System.out.printf("当前位置:纬度 %.6f, 经度 %.6f, 是否到达:%s\n", pos.latitude, pos.longitude, pos.reached ? "是" : "否"); } + /** + * 沿球面大圆,从起点(lat1, lon1)出发,沿着到终点(lat2, lon2)方向,距离为d(米)的位置点 + * 若d >= 起点到终点距离,则返回终点 + * @param lat1 起点纬度 + * @param lon1 起点经度 + * @param lat2 终点纬度 + * @param lon2 终点经度 + * @param d 距起点的距离(米) + * @return double[]{纬度, 经度} + */ + public static double[] pointAlong(double lat1, double lon1, double lat2, double lon2, double d) { + double L = haversine(lat1, lon1, lat2, lon2); + if (d <= 0) return new double[] {lat1, lon1}; + if (d >= L) return new double[] {lat2, lon2}; + double f = d / L; + return interpolate(lat1, lon1, lat2, lon2, f); + } } \ No newline at end of file diff --git a/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java b/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java new file mode 100644 index 0000000..1b00d81 --- /dev/null +++ b/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java @@ -0,0 +1,24 @@ +package com.hivekion.enums; + +import lombok.Getter; + +/** + * [类的简要说明] + *

+ * [详细描述,可选] + *

+ * + * @author LiDongYU + * @since 2025/7/22 + */ +public enum WsCmdTypeEnum { + PATH_UPDATE("path_update"), + PATH_FINISHED("path_finished"), + PATH_INIT("path_init"); + @Getter + private final String code; + + WsCmdTypeEnum(String code) { + this.code = code; + } +} diff --git a/src/main/java/com/hivekion/room/RoomManager.java b/src/main/java/com/hivekion/room/RoomManager.java index 14137e8..8e7d724 100644 --- a/src/main/java/com/hivekion/room/RoomManager.java +++ b/src/main/java/com/hivekion/room/RoomManager.java @@ -5,6 +5,7 @@ import com.hivekion.room.bean.Room; import com.hivekion.room.func.TaskAction; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; /** * [类的简要说明] @@ -60,4 +61,19 @@ public class RoomManager { } return 0; } + + public static void addFuture(ScheduledExecutorService future, String roomId) { + Room room = roomsMap.get(roomId); + if (room != null) { + room.addTaskReference(future); + } + } + + public static boolean isRunning(String id) { + Room room = roomsMap.get(id); + if (room != null) { + return room.isRunning(); + } + return false; + } } diff --git a/src/main/java/com/hivekion/room/bean/AbtParentTask.java b/src/main/java/com/hivekion/room/bean/AbtParentTask.java index 853336b..7b804d1 100644 --- a/src/main/java/com/hivekion/room/bean/AbtParentTask.java +++ b/src/main/java/com/hivekion/room/bean/AbtParentTask.java @@ -1,18 +1,11 @@ package com.hivekion.room.bean; -import com.hivekion.Global; +import com.hivekion.room.RoomManager; 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.*; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; import org.springframework.web.reactive.function.client.WebClient; /** @@ -28,7 +21,6 @@ import org.springframework.web.reactive.function.client.WebClient; public abstract class AbtParentTask implements TaskAction { - //任务数据 protected final ScenarioTask scenarioTask; //房间ID @@ -52,10 +44,10 @@ public abstract class AbtParentTask implements TaskAction { } - - - - + public void addScheduledExecutorServiceRefenceToRoom( + ScheduledExecutorService scheduledExecutorService) { + RoomManager.addFuture(scheduledExecutorService, this.roomId); + } @Override public void doSomeThing() { @@ -72,9 +64,30 @@ public abstract class AbtParentTask implements TaskAction { return scenarioTask.getTaskType(); } + //获取房间的持续时间 + public long getDuringTime() { + return RoomManager.getRoomDuringTime(this.roomId); + } + //获取房间状态 + public boolean getRoomStatus() { + return RoomManager.isRunning(roomId); + } + public void createBattleTaskOnTimingHandle(BizTaskOnTiming bizTaskOnTiming){ + ScheduledExecutorService schedule = Executors.newScheduledThreadPool( + 1); + schedule.scheduleWithFixedDelay(() -> { + bizTaskOnTiming.execTask(); + }, 0, 10, TimeUnit.SECONDS); + //房间统一管理定时器;房间关闭后,定时器销毁 + addScheduledExecutorServiceRefenceToRoom(schedule); + } +} + interface BizTaskOnTiming{ + + public void execTask(); } // 自定义线程工厂 @@ -94,4 +107,5 @@ class CustomThreadFactory implements ThreadFactory { thread.setPriority(Thread.NORM_PRIORITY); return thread; } + } \ No newline at end of file diff --git a/src/main/java/com/hivekion/room/bean/BattleRootTask.java b/src/main/java/com/hivekion/room/bean/BattleRootTask.java index de91523..7170479 100644 --- a/src/main/java/com/hivekion/room/bean/BattleRootTask.java +++ b/src/main/java/com/hivekion/room/bean/BattleRootTask.java @@ -1,7 +1,28 @@ package com.hivekion.room.bean; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.alibaba.fastjson2.JSONObject; import com.hivekion.Global; +import com.hivekion.common.entity.ResponseCmdInfo; +import com.hivekion.common.redis.RedisUtil; +import com.hivekion.common.uuid.IdUtils; import com.hivekion.scenario.entity.ScenarioTask; +import com.hivekion.statistic.bean.*; +import com.hivekion.statistic.service.StatisticService; +import com.hivekion.statistic.service.impl.StatisticServiceImpl; +import com.hivekion.supplier.entity.SupplierRequest; +import com.hivekion.supplier.service.ISupplierRequestService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * [类的简要说明] @@ -12,16 +33,185 @@ import com.hivekion.scenario.entity.ScenarioTask; * @author LiDongYU * @since 2025/7/22 */ +@Slf4j public class BattleRootTask extends AbtParentTask { + private StatisticService statisticService = null; + private RedisUtil redisUtil = null; + + private ISupplierRequestService supplierRequestService; + + private static final Double TEAM_SPREED = 1.2D; + private static final Double PERSON_SPREED = 3D; + private static final Double AMMUNITION_SPREED = 2.6D; + private static final Double FOOD_SPREED = 2.3D; + private static final Double WATER_SPREED = 3.6D; + private static final Double FUEL_SPREED = 3.6D; + private static final Double MEDICAL_SPREED = 1.6D; + + public BattleRootTask(ScenarioTask scenarioTask,String roomId) { super(scenarioTask,roomId); } + + private void initBean(){ + if(statisticService == null) { + statisticService = SpringUtil.getBean(StatisticService.class); + } + if(redisUtil == null) { + redisUtil = SpringUtil.getBean(RedisUtil.class); + } + if(supplierRequestService == null){ + supplierRequestService = SpringUtil.getBean(ISupplierRequestService.class); + } + } //执行一次 @Override public void doSomeThing() { - //会知道想定ID, resourceId; + log.info("===============begin BattleRootTask=========={}==========",this.getRoomStatus()); + this.initBean(); + if(this.getRoomStatus()) { + long initDuringTime = this.getDuringTime(); + StatisticBean statisticBean = null; + try { + statisticBean = statisticService.statistic(scenarioTask.getResourceId()); + }catch (Exception ex){ + ex.printStackTrace(); + } + + HashMap battleParams = new HashMap<>(); + +// statisticBean.getAmmunition().getCurrent() + //初始化本次战斗任务各种资源数 + + battleParams.put("ammunition",Double.valueOf(statisticBean.getAmmunition().getCurrent()).toString()); + battleParams.put("food",Double.valueOf(statisticBean.getFood().getCurrent()).toString()); + battleParams.put("fuel",Double.valueOf(statisticBean.getFuel().getCurrent()).toString()); + battleParams.put("medical",Double.valueOf(statisticBean.getMedical().getCurrent()).toString()); + battleParams.put("water",Double.valueOf(statisticBean.getWater().getCurrent()).toString()); + battleParams.put("person",Double.valueOf(statisticBean.getPerson().getCurrent()).toString()); + battleParams.put("teamLat",statisticBean.getTeam().getLat().toString()); + battleParams.put("teamLng",statisticBean.getTeam().getLng().toString()); + battleParams.put("duringTime",Long.valueOf(initDuringTime).toString()); + redisUtil.hset(scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(),battleParams); + log.info("===============================初始化本次战斗任务各种资源数===================================="); + double suppleAmount =statisticBean.getAmmunition().getTotal(); + double supplePerson =statisticBean.getPerson().getTotal(); + final Map suppleFlagMap = new HashMap<>(); + suppleFlagMap.put("ammunition",false); + suppleFlagMap.put("person",false); + //定时检查统计各种资源消耗量 + this.createBattleTaskOnTimingHandle(new BizTaskOnTiming() { + @Override + public void execTask() { + log.info("===============================定时检查统计各种资源消耗量 begin===================================="); + + Double personConsume = null; + Double ammunitionConsume = null; + Double foodConsume = null; + Double waterConsume = null; + Double fuelConsume = null; + Double medicalConsume = null; + String teamLat = null; + String teamLng = null; + JSONObject jsonObject = new JSONObject(); + try { + HashMap battleParams = (HashMap) redisUtil.hget(scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId()); + long duringTime = getDuringTime(); + long lastDuringTime = Long.valueOf(battleParams.get("duringTime").toString()); + long intervalDuringTime = duringTime - lastDuringTime; + double ammunition = Double.valueOf(battleParams.get("ammunition").toString()); + double food = Double.valueOf(battleParams.get("food").toString()); + double fuel = Double.valueOf(battleParams.get("fuel").toString()); + double medical = Double.valueOf(battleParams.get("medical").toString()); + double water = Double.valueOf(battleParams.get("water").toString()); + double person = Double.valueOf(battleParams.get("person").toString()); + teamLat = battleParams.get( "teamLat").toString(); + teamLng = battleParams.get( "teamLng").toString(); + // + personConsume = intervalDuringTime * PERSON_SPREED; + ammunitionConsume = intervalDuringTime * AMMUNITION_SPREED; + foodConsume = intervalDuringTime * FOOD_SPREED; + waterConsume = intervalDuringTime * WATER_SPREED; + fuelConsume = intervalDuringTime * FUEL_SPREED; + medicalConsume = intervalDuringTime * MEDICAL_SPREED; + + battleParams.put("ammunition",Double.valueOf(ammunition - ammunitionConsume).toString()); + battleParams.put("food",Double.valueOf(food - foodConsume).toString()); + battleParams.put("fuel",Double.valueOf(fuel - fuelConsume).toString()); + battleParams.put("medical",Double.valueOf(medical - medicalConsume).toString()); + battleParams.put("water",Double.valueOf(water - waterConsume).toString()); + battleParams.put("person",Double.valueOf(person - personConsume).toString()); + battleParams.put("duringTime",Long.valueOf(duringTime).toString()); + redisUtil.hset(scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), battleParams); + }catch (Exception ex){ + log.error("==============================设置消耗信息失败=============================================",ex.getMessage()); + } + try { + //推送消耗數據 + ResponseCmdInfo sendConsumeMsg = new ResponseCmdInfo<>(); + jsonObject.put("personConsume", personConsume); + jsonObject.put("ammunitionConsume", ammunitionConsume); + jsonObject.put("foodConsume", foodConsume); + jsonObject.put("waterConsume", waterConsume); + jsonObject.put("fuelConsume", fuelConsume); + jsonObject.put("medicalConsume", medicalConsume); + jsonObject.put("teamLat",teamLat); + jsonObject.put("teamLng",teamLng); + sendConsumeMsg.setData(jsonObject); + Global.sendCmdInfoQueue.add(sendConsumeMsg); + }catch (Exception ex){ + log.error("==================推送消耗數據 失败============================================",ex.getMessage()); + } + + try { + LocalDateTime currentDateTime = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); + String person = SpringUtil.getBean(Environment.class).getProperty("battle.person.warn"); + String ammunition = SpringUtil.getBean(Environment.class).getProperty("battle.ammunition.warn"); + log.info("===========person ammunition======={}====={}========",person,ammunition); +// String food = SpringUtil.getBean(Environment.class).getProperty("battle.food.warn"); +// String water = SpringUtil.getBean(Environment.class).getProperty("battle.water.warn"); +// String fuel = SpringUtil.getBean(Environment.class).getProperty("battle.fuel.warn"); +// String medical = SpringUtil.getBean(Environment.class).getProperty("battle.medical.warn"); + + if(Double.valueOf(battleParams.get("ammunition").toString()) <= Double.valueOf(ammunition) && suppleFlagMap.get("ammunition") == false){ + + SupplierRequest supplierRequest = new SupplierRequest(); + supplierRequest.setId(IdUtils.simpleUUID()); + supplierRequest.setFromResourceId(scenarioTask.getResourceId()); + supplierRequest.setSupplierNum(suppleAmount); + supplierRequest.setSupplierType("ammunition"); + supplierRequest.setGeneralTime(currentDateTime); + supplierRequest.setLat(jsonObject.get("teamLat").toString()); + supplierRequest.setLng(jsonObject.get("teamLng").toString()); + supplierRequestService.save(supplierRequest); + suppleFlagMap.put("ammunition",true) ; + } + if(Double.valueOf(battleParams.get("person").toString()) <= Double.valueOf(person) && suppleFlagMap.get("person") == false){ + SupplierRequest supplierRequest = new SupplierRequest(); + supplierRequest.setId(IdUtils.simpleUUID()); + supplierRequest.setFromResourceId(scenarioTask.getResourceId()); + supplierRequest.setSupplierNum(supplePerson); + supplierRequest.setSupplierType("person"); + supplierRequest.setGeneralTime(currentDateTime); + supplierRequest.setLat(jsonObject.get("teamLat").toString()); + supplierRequest.setLng(jsonObject.get("teamLng").toString()); + supplierRequestService.save(supplierRequest); + suppleFlagMap.put("person",true) ; + } + }catch (Exception ex){ + ex.printStackTrace(); + log.error("===========BattleRootTask supplierRequestService.saveBatch error====================",ex.getMessage()); + } + log.info("===============================定时检查统计各种资源消耗量 end===================================="); + } + }); + + + } + + } diff --git a/src/main/java/com/hivekion/room/bean/MoveRootTask.java b/src/main/java/com/hivekion/room/bean/MoveRootTask.java index 04230f0..052bf82 100644 --- a/src/main/java/com/hivekion/room/bean/MoveRootTask.java +++ b/src/main/java/com/hivekion/room/bean/MoveRootTask.java @@ -7,13 +7,21 @@ import com.alibaba.fastjson2.JSONObject; import com.hivekion.Global; import com.hivekion.common.MultiPointGeoPosition; import com.hivekion.common.entity.ResponseCmdInfo; +import com.hivekion.enums.WsCmdTypeEnum; import com.hivekion.room.func.TaskAction; import com.hivekion.scenario.entity.ScenarioTask; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; import java.util.TreeMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.core.env.Environment; @@ -29,70 +37,197 @@ import org.springframework.core.env.Environment; @Slf4j public class MoveRootTask extends AbtParentTask implements TaskAction { - protected final ScheduledExecutorService schedule = Executors.newScheduledThreadPool( - 1); - protected ScheduledFuture scheduledFuture; - private final double SPEED = 170; - private double accumulatedDistance = 0; + /** + * 速度 换算为100Km/小时 + */ + private final double SPEED = 27; + /** + * 距离和坐标的对应关系 + */ + private final TreeMap distanceInfoMap = new TreeMap<>(); + /** + * 开始点坐标 + */ + private final AtomicReference startPoint = new AtomicReference<>(); + public MoveRootTask(ScenarioTask scenarioTask, String roomId) { super(scenarioTask, roomId); } - private final Map distanceInfoMap = new TreeMap(); @Override public void doSomeThing() { log.info("move task running"); - //累计距离 + initPath(); //初始化路径 + updatePath(); //更新路径 + } + + /** + * 初始化路径 + */ + private void initPath() { + try { + + 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"); + JSONArray coordinates = pointsObj.getJSONArray("coordinates"); + //组装信息 + Map dataMap = new HashMap<>(); + dataMap.put("resourceId", scenarioTask.getResourceId()); + dataMap.put("points", coordinates); + //推送路径任务 + Global.sendCmdInfoQueue.add( + ResponseCmdInfo.create(WsCmdTypeEnum.PATH_INIT.getCode(), roomId, + scenarioTask.getScenarioId(), dataMap)); + log.info("init::{}", JSON.toJSONString(coordinates)); + //计算各个点的累计距离和坐标的对应关系 + double beforeLng = Double.parseDouble(scenarioTask.getFromLng()); + double beforeLat = Double.parseDouble(scenarioTask.getFromLat()); + double total = 0; + for (int i = 0; i < coordinates.size(); i++) { + JSONArray coordinate = coordinates.getJSONArray(i); + Double lng = coordinate.getDouble(0); + + Double lat = coordinate.getDouble(1); + + double distance = MultiPointGeoPosition.haversine(beforeLat, beforeLng, lat, lng); + //当前总距离 + total = total + distance; + //定义坐标对象 + Coordinate coordinateInfo = new Coordinate(); + coordinateInfo.setLat(lat); + coordinateInfo.setLng(lng); + + //记录距离和数组列表直接的索引关系 + distanceInfoMap.put(total, coordinateInfo); + + beforeLng = lng; + beforeLat = lat; - 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; - + //设置第一个开始位置 + startPoint.set(distanceInfoMap.firstKey()); } + + } catch (Exception e) { + log.error("error::", e); } + } + + private void updatePath() { + + ScheduledExecutorService schedule = Executors.newScheduledThreadPool( + 1); + schedule.scheduleWithFixedDelay(() -> { + + try { + if (this.getRoomStatus()) { + + long duringTime = getDuringTime(); + log.info("duringTime::{}", duringTime); + //跑动距离 + double distance = duringTime * SPEED; + //获取大与此距离的第一个路线点key + Entry endPoint = distanceInfoMap.ceilingEntry(distance); + //ws数据 + List dataList = new ArrayList<>(); + HashMap dataMap = new HashMap<>(); + dataMap.put("resourceId", scenarioTask.getResourceId()); + dataMap.put("points", dataList); + + if (Double.compare(distance, endPoint.getKey()) < 0) { + //获取小于最大值的第一个key + Double lowerKey = distanceInfoMap.lowerKey(endPoint.getKey()); + // log.info("distance::{},lowerKey::{},endPoint{}",distance,lowerKey,endPoint.getKey()); + //获取从上一个开始节点到lowKey的数据 + NavigableMap subPathMap = distanceInfoMap.subMap(startPoint.get(), + true, lowerKey, true); + for (Double key : subPathMap.keySet()) { + Coordinate coordinate = subPathMap.get(key); + dataList.add(new double[]{coordinate.getLng(), coordinate.getLat()}); + } + double diff =distance - lowerKey ; + + //插入值 + double[] insertPoints = MultiPointGeoPosition.pointAlong( + distanceInfoMap.get(lowerKey).getLat(), distanceInfoMap.get(lowerKey).getLng(), + endPoint.getValue().getLat(), endPoint.getValue().getLng(), diff); + + dataList.add(new double[]{insertPoints[1], insertPoints[0]}); + + Coordinate coordinate = new Coordinate(); + coordinate.setLat(insertPoints[0]); + coordinate.setLng(insertPoints[1]); + distanceInfoMap.put(distance, coordinate); + startPoint.set(distance); + + + Global.sendCmdInfoQueue.add( + ResponseCmdInfo.create(WsCmdTypeEnum.PATH_UPDATE.getCode(), roomId, + scenarioTask.getScenarioId(), dataMap)); + + } else if (Double.compare(distance, endPoint.getKey()) == 0) { + NavigableMap subPathMap = distanceInfoMap.subMap(startPoint.get(), + true, endPoint.getKey(), true); + for (Double key : subPathMap.keySet()) { + Coordinate coordinate = subPathMap.get(key); + dataList.add(new double[]{coordinate.getLng(), coordinate.getLat()}); + } + + startPoint.set(endPoint.getKey()); + Global.sendCmdInfoQueue.add( + ResponseCmdInfo.create(WsCmdTypeEnum.PATH_UPDATE.getCode(), roomId, + scenarioTask.getScenarioId(), dataMap)); + + } else { + //完成路径 + Global.sendCmdInfoQueue.add( + ResponseCmdInfo.create(WsCmdTypeEnum.PATH_FINISHED.getCode(), roomId, + scenarioTask.getScenarioId(), dataMap)); + } + + + + } + } catch (Exception e) { + log.error("error::", e); + } + + + }, 0, 1, TimeUnit.SECONDS); + + //房间统一管理定时器;房间关闭后,定时器销毁 + addScheduledExecutorServiceRefenceToRoom(schedule); } +} +@Data +class Coordinate { - - + double lng; + double lat; } diff --git a/src/main/java/com/hivekion/room/bean/Room.java b/src/main/java/com/hivekion/room/bean/Room.java index f7200de..5ac43d9 100644 --- a/src/main/java/com/hivekion/room/bean/Room.java +++ b/src/main/java/com/hivekion/room/bean/Room.java @@ -20,6 +20,7 @@ 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.AtomicLong; import lombok.Data; import lombok.extern.slf4j.Slf4j; @@ -37,6 +38,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class Room implements AutoCloseable { + private AtomicBoolean status = new AtomicBoolean(false); /** * 任务管理相关 */ @@ -56,6 +58,8 @@ public class Room implements AutoCloseable { 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( @@ -79,14 +83,13 @@ public class Room implements AutoCloseable { private int mag = 1; - /** * 启动 * * @param time 总时间 */ public void start(long time) { - + status.set(true); totalTime.set(time); startTask(); } @@ -95,6 +98,7 @@ public class Room implements AutoCloseable { * 停止 */ public void stop() { + status.set(false); cancelTask(); } @@ -102,16 +106,19 @@ public class Room implements AutoCloseable { * 暂停 */ public void pause() { + status.set(false); cancelTask(); } public void resume() { + status.set(true); startTask(); } public long getDuringTime() { return duringTime.get(); } + public long getTotalTime() { return totalTime.get(); } @@ -120,21 +127,22 @@ public class Room implements AutoCloseable { private void startTask() { if (future == null || future.isCancelled()) { future = scheduler.scheduleAtFixedRate(() -> { - ScenarioWsParam magValue = Global.roomParamMap.get(this.scenario.getId() + "_" + this.roomId); - if(magValue!=null){ + ScenarioWsParam magValue = Global.roomParamMap.get( + this.scenario.getId() + "_" + this.roomId); + if (magValue != null) { this.mag = magValue.getMag(); } - + log.info("mag:{}", mag); long curTime = duringTime.addAndGet(this.mag); sendRemainTime((totalTime.get() - curTime)); NavigableMap> actions = actionMap.headMap(curTime, true); - if (!actions.isEmpty() ) { + if (!actions.isEmpty()) { actions.forEach((key, action) -> { - action.forEach((taskAction, task) -> { - actionExecutor.submit(task::doSomeThing); - }); + action.forEach((taskAction, task) -> { + actionExecutor.submit(task::doSomeThing); + }); }); actions.clear(); @@ -170,14 +178,23 @@ public class Room implements AutoCloseable { 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()))); + 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(); + } } diff --git a/src/main/java/com/hivekion/scenario/service/impl/ScenarioTaskServiceImpl.java b/src/main/java/com/hivekion/scenario/service/impl/ScenarioTaskServiceImpl.java index cd9524c..8c9814c 100644 --- a/src/main/java/com/hivekion/scenario/service/impl/ScenarioTaskServiceImpl.java +++ b/src/main/java/com/hivekion/scenario/service/impl/ScenarioTaskServiceImpl.java @@ -148,7 +148,7 @@ public class ScenarioTaskServiceImpl extends try { long diff = Duration.between(scenario.getStartTime(),task.getStartTime()) .getSeconds(); - log.info("diff::{},taskType::{}",diff,task.getTaskType()); + switch (task.getTaskType()) { //移动任务 case "1": diff --git a/src/main/java/com/hivekion/statistic/service/impl/StatisticServiceImpl.java b/src/main/java/com/hivekion/statistic/service/impl/StatisticServiceImpl.java index f296fbf..c84fa0f 100644 --- a/src/main/java/com/hivekion/statistic/service/impl/StatisticServiceImpl.java +++ b/src/main/java/com/hivekion/statistic/service/impl/StatisticServiceImpl.java @@ -1,5 +1,6 @@ package com.hivekion.statistic.service.impl; +import cn.hutool.core.collection.CollectionUtil; import com.hivekion.baseData.entity.Fightpowerstaff; import com.hivekion.baseData.entity.OrgSupplier; import com.hivekion.baseData.service.FightpowerstaffService; @@ -61,13 +62,21 @@ public class StatisticServiceImpl implements StatisticService { List orgPostList = scenarioOrgPostService.selectByCondition(post); List orgList = orgPostList.stream().map(ScenarioOrgPost::getOrgId) .collect(Collectors.toList()); + if(CollectionUtil.isEmpty(orgList)){ + return new StatisticBean(); + } //获取人员信息 - List staffList = fightpowerstaffService.queryByOrgIds(orgList); - int sum = staffList.stream() - .mapToInt(Fightpowerstaff::getNumber) - .sum(); - statisticBean.getPerson().setCurrent(sum); - statisticBean.getPerson().setTotal(sum); + if(!orgList.isEmpty()){ + List staffList = fightpowerstaffService.queryByOrgIds(orgList); + int sum = staffList.stream() + .mapToInt(Fightpowerstaff::getNumber) + .sum(); + statisticBean.getPerson().setCurrent(sum); + statisticBean.getPerson().setTotal(sum); + }else{ + return statisticBean; + } + //获取物资信息 List suppliers = orgSupplierService.selectByOrgIds(orgList); diff --git a/src/main/java/com/hivekion/supplier/controller/SupplierRequestController.java b/src/main/java/com/hivekion/supplier/controller/SupplierRequestController.java new file mode 100644 index 0000000..477a3b7 --- /dev/null +++ b/src/main/java/com/hivekion/supplier/controller/SupplierRequestController.java @@ -0,0 +1,18 @@ +package com.hivekion.supplier.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.stereotype.Controller; + +/** + *

+ * 前端控制器 + *

+ * + * @author liDongYu + * @since 2025-09-18 + */ +@Controller +@RequestMapping("/supplier/supplierRequest") +public class SupplierRequestController { + +} diff --git a/src/main/java/com/hivekion/supplier/entity/SupplierRequest.java b/src/main/java/com/hivekion/supplier/entity/SupplierRequest.java new file mode 100644 index 0000000..82f66da --- /dev/null +++ b/src/main/java/com/hivekion/supplier/entity/SupplierRequest.java @@ -0,0 +1,110 @@ +package com.hivekion.supplier.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import java.io.Serializable; +import java.time.LocalDateTime; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + *

+ * + *

+ * + * @author liDongYu + * @since 2025-09-18 + */ +@TableName("TBL_SUPPLIER_REQUEST") +@ApiModel(value = "SupplierRequest对象", description = "") +public class SupplierRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableField(value="ID") + private String id; + + @TableField(value="GENERAL_TIME") + private LocalDateTime generalTime; + + @TableField(value="FROM_RESOURCE_ID") + private String fromResourceId; + + @TableField(value="SUPPLIER_TYPE") + private String supplierType; + @TableField(value="SUPPLIER_NUM") + private Double supplierNum; + @TableField(value="LAT") + private String lat; + @TableField(value="LNG") + private String lng; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public LocalDateTime getGeneralTime() { + return generalTime; + } + + public void setGeneralTime(LocalDateTime generalTime) { + this.generalTime = generalTime; + } + + public String getFromResourceId() { + return fromResourceId; + } + + public void setFromResourceId(String fromResourceId) { + this.fromResourceId = fromResourceId; + } + + public String getSupplierType() { + return supplierType; + } + + public void setSupplierType(String supplierType) { + this.supplierType = supplierType; + } + + public Double getSupplierNum() { + return supplierNum; + } + + public void setSupplierNum(Double supplierNum) { + this.supplierNum = supplierNum; + } + + public String getLat() { + return lat; + } + + public void setLat(String lat) { + this.lat = lat; + } + + public String getLng() { + return lng; + } + + public void setLng(String lng) { + this.lng = lng; + } + + @Override + public String toString() { + return "SupplierRequest{" + + "id = " + id + + ", generalTime = " + generalTime + + ", fromResourceId = " + fromResourceId + + ", supplierType = " + supplierType + + ", supplierNum = " + supplierNum + + ", lat = " + lat + + ", lng = " + lng + + "}"; + } +} diff --git a/src/main/java/com/hivekion/supplier/mapper/SupplierRequestMapper.java b/src/main/java/com/hivekion/supplier/mapper/SupplierRequestMapper.java new file mode 100644 index 0000000..9d11777 --- /dev/null +++ b/src/main/java/com/hivekion/supplier/mapper/SupplierRequestMapper.java @@ -0,0 +1,16 @@ +package com.hivekion.supplier.mapper; + +import com.hivekion.supplier.entity.SupplierRequest; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author liDongYu + * @since 2025-09-18 + */ +public interface SupplierRequestMapper extends BaseMapper { + +} diff --git a/src/main/java/com/hivekion/supplier/service/ISupplierRequestService.java b/src/main/java/com/hivekion/supplier/service/ISupplierRequestService.java new file mode 100644 index 0000000..15bfc36 --- /dev/null +++ b/src/main/java/com/hivekion/supplier/service/ISupplierRequestService.java @@ -0,0 +1,16 @@ +package com.hivekion.supplier.service; + +import com.hivekion.supplier.entity.SupplierRequest; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 服务类 + *

+ * + * @author liDongYu + * @since 2025-09-18 + */ +public interface ISupplierRequestService extends IService { + +} diff --git a/src/main/java/com/hivekion/supplier/service/impl/SupplierRequestServiceImpl.java b/src/main/java/com/hivekion/supplier/service/impl/SupplierRequestServiceImpl.java new file mode 100644 index 0000000..2340c89 --- /dev/null +++ b/src/main/java/com/hivekion/supplier/service/impl/SupplierRequestServiceImpl.java @@ -0,0 +1,20 @@ +package com.hivekion.supplier.service.impl; + +import com.hivekion.supplier.entity.SupplierRequest; +import com.hivekion.supplier.mapper.SupplierRequestMapper; +import com.hivekion.supplier.service.ISupplierRequestService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author liDongYu + * @since 2025-09-18 + */ +@Service +public class SupplierRequestServiceImpl extends ServiceImpl implements ISupplierRequestService { + +} diff --git a/src/main/java/com/hivekion/ws/WsServer.java b/src/main/java/com/hivekion/ws/WsServer.java index 0d4ab97..9e11408 100644 --- a/src/main/java/com/hivekion/ws/WsServer.java +++ b/src/main/java/com/hivekion/ws/WsServer.java @@ -32,7 +32,7 @@ public class WsServer { // key -> 当前key下所有会话 private static final Map>> SESSION_MAP = new ConcurrentHashMap<>(); - private final Object lock = new Object(); + private static final Object lock = new Object(); @OnOpen @@ -112,7 +112,7 @@ public class WsServer { public static void sendMessage(Integer scenarioId, String room, String message) { // log.info("send {},{},{}", message, scenarioId, room); - Object lock = lockMap.computeIfAbsent(scenarioId, k -> new Object()); + synchronized (lock) { Map> roomMap = SESSION_MAP.get(String.valueOf(scenarioId)); // log.info("roomMap:{}", roomMap); diff --git a/src/main/resources/application-rule.properties b/src/main/resources/application-rule.properties new file mode 100644 index 0000000..094db2a --- /dev/null +++ b/src/main/resources/application-rule.properties @@ -0,0 +1,6 @@ +battle.person.warn = 2 +battle.ammunition.warn = 3 +battle.food.warn = 3 +battle.water.warn = 3 +battle.fuel.warn = 2 +battle.medical.warn = 1 \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 196b48f..c02e11b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,2 +1,2 @@ -spring.profiles.active=prod +spring.profiles.active=prod,rule #spring.profiles.active=dev \ No newline at end of file