diff --git a/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java b/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java index 1b00d81..257bc33 100644 --- a/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java +++ b/src/main/java/com/hivekion/enums/WsCmdTypeEnum.java @@ -14,6 +14,7 @@ import lombok.Getter; public enum WsCmdTypeEnum { PATH_UPDATE("path_update"), PATH_FINISHED("path_finished"), + STATISTIC("statistic"), PATH_INIT("path_init"); @Getter private final String code; diff --git a/src/main/java/com/hivekion/room/bean/AbtParentTask.java b/src/main/java/com/hivekion/room/bean/AbtParentTask.java index c60c04e..8016256 100644 --- a/src/main/java/com/hivekion/room/bean/AbtParentTask.java +++ b/src/main/java/com/hivekion/room/bean/AbtParentTask.java @@ -1,9 +1,28 @@ 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.baseData.entity.Scenario; +import com.hivekion.baseData.service.ScenarioService; +import com.hivekion.common.MultiPointGeoPosition; +import com.hivekion.common.entity.ResponseCmdInfo; +import com.hivekion.common.redis.RedisUtil; +import com.hivekion.enums.WsCmdTypeEnum; import com.hivekion.room.RoomManager; import com.hivekion.room.func.TaskAction; import com.hivekion.scenario.entity.ScenarioTask; import com.hivekion.statistic.bean.StatisticBean; +import java.time.Duration; +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.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; @@ -11,6 +30,9 @@ 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.AtomicReference; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.env.Environment; import org.springframework.web.reactive.function.client.WebClient; /** @@ -22,16 +44,26 @@ import org.springframework.web.reactive.function.client.WebClient; * @author LiDongYU * @since 2025/7/22 */ - +@Slf4j public abstract class AbtParentTask implements TaskAction { - - + /** + * 开始点坐标 + */ + private final AtomicReference startPoint = new AtomicReference<>(); + /** + * 距离和坐标的对应关系 + */ + protected final TreeMap distanceInfoMap = new TreeMap<>(); //任务数据 protected final ScenarioTask scenarioTask; //房间ID protected final String roomId; //http请求 protected WebClient webClient = WebClient.create(); + /** + * 任务相对与想定的开始时间 + */ + private long taskRelativeTime = 0; //线程池 protected ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, // 核心线程数 @@ -46,7 +78,10 @@ public abstract class AbtParentTask implements TaskAction { public AbtParentTask(ScenarioTask scenarioTask, String roomId) { this.scenarioTask = scenarioTask; this.roomId = roomId; - + Scenario scenario = SpringUtil.getBean(ScenarioService.class) + .getScenarioById(scenarioTask.getScenarioId()); + taskRelativeTime = Math.abs( + Duration.between(scenario.getStartTime(), scenarioTask.getStartTime()).getSeconds()); } public void addScheduledExecutorServiceRefenceToRoom( @@ -89,9 +124,163 @@ public abstract class AbtParentTask implements TaskAction { addScheduledExecutorServiceRefenceToRoom(schedule); } - protected void pushStatistic() { - StatisticBean statisticBean = new StatisticBean(); - //获取分队信息 + + protected 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)); + + SpringUtil.getBean(RedisUtil.class).hset( + scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), + "init_path", 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; + + } + //设置第一个开始位置 + startPoint.set(distanceInfoMap.firstKey()); + } + + } catch (Exception e) { + log.error("error::", e); + } + } + protected void updatePath(double speed) { + + ScheduledExecutorService schedule = Executors.newScheduledThreadPool( + 1); + schedule.scheduleWithFixedDelay(() -> { + + try { + if (this.getRoomStatus()) { + + long duringTime = getDuringTime() - taskRelativeTime; + + 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); + SpringUtil.getBean(RedisUtil.class).hset( + scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), + "position", JSON.toJSONString(coordinate)); + + 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); } } diff --git a/src/main/java/com/hivekion/room/bean/BattleRootTask.java b/src/main/java/com/hivekion/room/bean/BattleRootTask.java index b14dae1..d83d400 100644 --- a/src/main/java/com/hivekion/room/bean/BattleRootTask.java +++ b/src/main/java/com/hivekion/room/bean/BattleRootTask.java @@ -189,7 +189,7 @@ public class BattleRootTask extends AbtParentTask { SupplierRequest supplierRequest = new SupplierRequest(); supplierRequest.setId(IdUtils.simpleUUID()); supplierRequest.setFromResourceId(scenarioTask.getResourceId()); - supplierRequest.setSupplierNum(String.valueOf(suppleAmount)); + supplierRequest.setSupplierNum(Double.valueOf(String.valueOf(suppleAmount))); supplierRequest.setSupplierType("ammunition"); supplierRequest.setGeneralTime(currentDateTime); supplierRequest.setLat(jsonObject.get("teamLat").toString()); @@ -201,7 +201,7 @@ public class BattleRootTask extends AbtParentTask { SupplierRequest supplierRequest = new SupplierRequest(); supplierRequest.setId(IdUtils.simpleUUID()); supplierRequest.setFromResourceId(scenarioTask.getResourceId()); - supplierRequest.setSupplierNum(String.valueOf(suppleDeath)); + supplierRequest.setSupplierNum(Double.valueOf(String.valueOf(suppleDeath))); supplierRequest.setSupplierType("death"); supplierRequest.setGeneralTime(currentDateTime); supplierRequest.setLat(jsonObject.get("teamLat").toString()); @@ -213,7 +213,7 @@ public class BattleRootTask extends AbtParentTask { SupplierRequest supplierRequest = new SupplierRequest(); supplierRequest.setId(IdUtils.simpleUUID()); supplierRequest.setFromResourceId(scenarioTask.getResourceId()); - supplierRequest.setSupplierNum(String.valueOf(suppleInjured)); + supplierRequest.setSupplierNum(Double.valueOf(String.valueOf(suppleInjured))); supplierRequest.setSupplierType("injured"); supplierRequest.setGeneralTime(currentDateTime); supplierRequest.setLat(jsonObject.get("teamLat").toString()); diff --git a/src/main/java/com/hivekion/room/bean/Coordinate.java b/src/main/java/com/hivekion/room/bean/Coordinate.java new file mode 100644 index 0000000..bf656df --- /dev/null +++ b/src/main/java/com/hivekion/room/bean/Coordinate.java @@ -0,0 +1,19 @@ +package com.hivekion.room.bean; + +import lombok.Data; + +/** + * [类的简要说明] + *

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

+ * + * @author LiDongYU + * @since 2025/7/22 + */ +@Data +public class Coordinate { + + private double lng; + private double lat; +} diff --git a/src/main/java/com/hivekion/room/bean/MoveRootTask.java b/src/main/java/com/hivekion/room/bean/MoveRootTask.java index 0f9a9b2..79c4f27 100644 --- a/src/main/java/com/hivekion/room/bean/MoveRootTask.java +++ b/src/main/java/com/hivekion/room/bean/MoveRootTask.java @@ -47,22 +47,12 @@ public class MoveRootTask extends AbtParentTask implements TaskAction { * 速度 换算为100Km/小时 */ private final double SPEED = 27; - /** - * 距离和坐标的对应关系 - */ - private final TreeMap distanceInfoMap = new TreeMap<>(); - /** - * 开始点坐标 - */ - private final AtomicReference startPoint = new AtomicReference<>(); - /** - * 任务相对与想定的开始时间 - */ - private long taskRelativeStartTime = 0; + /** * 油料消耗速率 */ private double fuelConsumption = 0; + private double fuelThreshold = 0; /** * 消耗任务间隔 */ @@ -85,7 +75,7 @@ public class MoveRootTask extends AbtParentTask implements TaskAction { initEnv(); //初始化环境 initPath(); //初始化路径 - updatePath(); //更新路径 + updatePath(SPEED); //更新路径 fuelConsumption();//油品消耗 } @@ -93,204 +83,52 @@ public class MoveRootTask extends AbtParentTask implements TaskAction { * 初始化环境 */ private void initEnv() { - //获取任务相对于想定的开始时间 - Scenario scenario = SpringUtil.getBean(ScenarioService.class) - .getScenarioById(scenarioTask.getScenarioId()); - taskRelativeStartTime = Math.abs( - Duration.between(scenario.getStartTime(), scenarioTask.getStartTime()).getSeconds()); + + //获取油品消耗规则 String fuelConsumptionStr = SpringUtil.getBean(Environment.class) - .getProperty("vehicle.fuel.consumption.per.km"); + .getProperty("fuel_spreed"); fuelConsumption = Double.parseDouble(fuelConsumptionStr == null ? "0" : fuelConsumptionStr); - - statisticBean = SpringUtil.getBean(StatisticServiceImpl.class).statistic(scenarioTask.getResourceId()); + fuelThreshold = Double.parseDouble(SpringUtil.getBean(Environment.class) + .getProperty("fuel.warn ","0")); + statisticBean = SpringUtil.getBean(StatisticServiceImpl.class) + .statistic(scenarioTask.getResourceId()); } - /** - * 初始化路径 - */ - 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)); - - redis.hset( - scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), - "init_path", 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; - - } - //设置第一个开始位置 - 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() - taskRelativeStartTime; - - 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); - redis.hset( - scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), - "position", JSON.toJSONString(coordinate)); - - 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); - } - private void fuelConsumption() { ScheduledExecutorService schedule = Executors.newScheduledThreadPool( 1); schedule.scheduleWithFixedDelay(() -> { if (getRoomStatus()) { - double total = consumptionTaskInterval * SPEED / 1000 * fuelConsumption; + double currentUseUp = consumptionTaskInterval * SPEED / 1000 * fuelConsumption; + + + //更新redis中油品的消耗 Object currentFuelObj = redis.hget( scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), - "fuel"); + "fuelConsume"); if (currentFuelObj != null) { double fuel = Double.parseDouble(currentFuelObj.toString()); - if (fuel > 0) { - fuel = fuel - total < 0 ? 0 : fuel - total; + fuel = fuel + currentUseUp; //更新值 redis.hset( scenarioTask.getScenarioId() + "-" + roomId + "-" + scenarioTask.getResourceId(), - "fuel", fuel); - //推送前端 + "fuelConsume", fuel); + + double totalFuel = statisticBean.getFuel().getTotal(); + if(fuel*100/totalFuel