From f31f0991c0d2036e563b886f57de4cf45d3c72cb Mon Sep 17 00:00:00 2001
From: dcb <xgybdcb@163.com>
Date: 星期二, 01 七月 2025 14:51:59 +0800
Subject: [PATCH] 实时模拟异步功能实现

---
 src/main/java/com/se/nsl/controller/SimuController.java              |   67 ++++
 src/main/resources/mapper/SimuMapper.xml                             |    4 
 src/main/java/com/se/nsl/config/AsyncExecutor.java                   |   21 +
 src/main/java/com/se/nsl/service/ResolveService.java                 |  125 +-------
 src/main/java/com/se/nsl/mapper/SimuMapper.java                      |    7 
 src/main/java/com/se/nsl/config/PropertiesConfig.java                |   10 
 src/main/java/com/se/nsl/service/RealTimeSimulationService.java      |    3 
 src/main/resources/application-dev.yml                               |    1 
 src/main/java/com/se/nsl/domain/vo/ConfigVo.java                     |   11 
 src/main/java/com/se/nsl/service/RealTimeSimulationAsyncService.java |  572 ++++++++++++++++++++++++++++++++++++++++
 10 files changed, 702 insertions(+), 119 deletions(-)

diff --git a/src/main/java/com/se/nsl/config/AsyncExecutor.java b/src/main/java/com/se/nsl/config/AsyncExecutor.java
new file mode 100644
index 0000000..5bebab8
--- /dev/null
+++ b/src/main/java/com/se/nsl/config/AsyncExecutor.java
@@ -0,0 +1,21 @@
+package com.se.nsl.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+@Configuration
+public class AsyncExecutor {
+    @Bean(name = "realTimeExecutor")
+    public Executor realTimeSimulationExecutor() {
+        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+        executor.setCorePoolSize(5);
+        executor.setMaxPoolSize(10);
+        executor.setQueueCapacity(25);
+        executor.setThreadNamePrefix("RealTimeExecutor-");
+        executor.initialize();
+        return executor;
+    }
+}
diff --git a/src/main/java/com/se/nsl/config/PropertiesConfig.java b/src/main/java/com/se/nsl/config/PropertiesConfig.java
index c1f7f51..6d50206 100644
--- a/src/main/java/com/se/nsl/config/PropertiesConfig.java
+++ b/src/main/java/com/se/nsl/config/PropertiesConfig.java
@@ -102,6 +102,8 @@
 
     private Double saveFilter;
 
+    private Double evaporation;
+
     public String getVer() {
         return ver;
     }
@@ -473,4 +475,12 @@
     public void setSaveFilter(Double saveFilter) {
         this.saveFilter = saveFilter;
     }
+
+    public Double getEvaporation() {
+        return evaporation;
+    }
+
+    public void setEvaporation(Double evaporation) {
+        this.evaporation = evaporation;
+    }
 }
diff --git a/src/main/java/com/se/nsl/controller/SimuController.java b/src/main/java/com/se/nsl/controller/SimuController.java
index f4d0076..f289da9 100644
--- a/src/main/java/com/se/nsl/controller/SimuController.java
+++ b/src/main/java/com/se/nsl/controller/SimuController.java
@@ -6,9 +6,7 @@
 import com.se.nsl.domain.po.Simu;
 import com.se.nsl.domain.po.SimuData;
 import com.se.nsl.domain.vo.*;
-import com.se.nsl.service.RealTimeSimulationService;
-import com.se.nsl.service.ResolveService;
-import com.se.nsl.service.SimuService;
+import com.se.nsl.service.*;
 import com.se.nsl.utils.SimulateType;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -34,7 +32,10 @@
     ResolveService resolveService;
 
     @Resource
-    RealTimeSimulationService rts;
+    RealTimeSimulationAsyncService rtsas;
+
+    @Resource
+    CrossSectionAnalysisService scas;
 
     /**
      * 鍒嗛〉鏌ヨ鎺ㄦ紨妯℃嫙
@@ -123,7 +124,7 @@
             if (null == id || id < 1) return fail("id涓虹┖");
 
             Simu simu = simuService.selectById(id);
-            if (null == simu) return fail("鏂规鎵句笉鍒�");
+            if (null == simu) return notFound("鏂规鎵句笉鍒�");
             if (StringUtils.isEmpty(simu.getData())) return fail("鏂规鏁版嵁(JSON)涓虹┖");
 
             SimuData data = JSON.parseObject(simu.getData(), SimuData.class);
@@ -138,8 +139,8 @@
                 int rows = resolveService.start(simu);
                 return success("ok");
             } else if (simulateType == SimulateType.REAL_TIME) {
-                String s = rts.realTimeSimulate(simu);
-                return success(s);
+                rtsas.startSimulation(simu);
+                return success(null, "瀹炴椂妯℃嫙浠诲姟宸叉彁浜�");
             }
             return fail("妯℃嫙绫诲瀷鏆備笉鏀寔");
         } catch (Exception ex) {
@@ -201,4 +202,56 @@
             return success(simuResults, simuResults.size());
         }
     }
+
+    @ApiOperation(value = "crossSection")
+    @GetMapping("/crossSection")
+    public R<Object> crossSection(String serviceName, double[] startPoint, double[] endPoint) {
+        if (serviceName == null) {
+            return clientError("鏈嶅姟鍚嶄笉鑳戒负绌�");
+        }
+        if (startPoint == null) {
+            return clientError("璧风偣涓嶈兘涓虹┖");
+        }
+        if (startPoint.length < 2) {
+            return clientError("璧风偣鑷冲皯鍖呭惈x,y涓や釜鍊�");
+        }
+        if (endPoint == null) {
+            return clientError("缁堢偣涓嶈兘涓虹┖");
+        }
+        if (endPoint.length < 2) {
+            return clientError("缁堢偣鑷冲皯鍖呭惈x,y涓や釜鍊�");
+        }
+        List<CrossSectionAnalysisResult> result = scas.crossSectionAnalysis(serviceName, startPoint, endPoint);
+        return success(result);
+    }
+
+    @ApiOperation(value = "stop")
+    @GetMapping("/stop")
+    public R<Object> stop(Integer id) {
+        if (id == null) {
+            return fail("id涓嶈兘涓虹┖");
+        }
+        try {
+            rtsas.stopSimulation(id);
+        } catch (IllegalArgumentException e) {
+            return notFound(e.getMessage());
+        }
+        return success(null, "姝e湪鍋滄id涓�" + id + "鐨勬ā鎷熶换鍔�");
+    }
+
+    @ApiOperation(value = "results")
+    @GetMapping("/results")
+    public R<Object> querySimulationResult(Integer id) {
+        if (null == id || id < 1) return clientError("id涓嶈兘涓虹┖");
+        Simu simu = simuService.selectById(id);
+        if (simu == null) {
+            return clientError("鎵句笉鍒板搴旂殑鏈嶅姟");
+        }
+        String serviceName = simu.getServiceName();
+        if (serviceName == null) {
+            return fail("鎵句笉鍒板搴旂殑鏈嶅姟");
+        }
+        List<String> results = resolveService.simulationResults(serviceName);
+        return success(results, results.size());
+    }
 }
diff --git a/src/main/java/com/se/nsl/domain/vo/ConfigVo.java b/src/main/java/com/se/nsl/domain/vo/ConfigVo.java
index bbf08e7..022994b 100644
--- a/src/main/java/com/se/nsl/domain/vo/ConfigVo.java
+++ b/src/main/java/com/se/nsl/domain/vo/ConfigVo.java
@@ -29,6 +29,8 @@
 
     private Boolean variable_dt;
 
+    private double evaporation;
+
     private ResultVo result;
 
     public ConfigVo() {
@@ -44,6 +46,7 @@
         this.infiltration = "case1/landuse_to_infiltration.dat";
         this.dt = 0.1;
         this.variable_dt = true;
+        this.evaporation = 0;
     }
 
     public ConfigVo(String terrain, String landuse, String station,
@@ -166,4 +169,12 @@
     public void setResult(ResultVo result) {
         this.result = result;
     }
+
+    public double getEvaporation() {
+        return evaporation;
+    }
+
+    public void setEvaporation(double evaporation) {
+        this.evaporation = evaporation;
+    }
 }
diff --git a/src/main/java/com/se/nsl/mapper/SimuMapper.java b/src/main/java/com/se/nsl/mapper/SimuMapper.java
index fbaf140..2617b2a 100644
--- a/src/main/java/com/se/nsl/mapper/SimuMapper.java
+++ b/src/main/java/com/se/nsl/mapper/SimuMapper.java
@@ -18,4 +18,11 @@
     int inserts(@Param("list") List<Simu> list);
 
     int updates(@Param("list") List<Simu> list);
+
+    /**
+     * 鏇存柊妯℃嫙缁撴灉涓璻esult瀛楁鐘舵��
+     * @param simu
+     */
+    void updateResult(Simu simu);
+
 }
diff --git a/src/main/java/com/se/nsl/service/RealTimeSimulationAsyncService.java b/src/main/java/com/se/nsl/service/RealTimeSimulationAsyncService.java
new file mode 100644
index 0000000..3dea2ae
--- /dev/null
+++ b/src/main/java/com/se/nsl/service/RealTimeSimulationAsyncService.java
@@ -0,0 +1,572 @@
+package com.se.nsl.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.se.nsl.config.PropertiesConfig;
+import com.se.nsl.config.RealTimeSimulationConfig;
+import com.se.nsl.domain.dto.*;
+import com.se.nsl.domain.po.RainGauge;
+import com.se.nsl.domain.po.Simu;
+import com.se.nsl.domain.po.SimuData;
+import com.se.nsl.domain.vo.ConfigVo;
+import com.se.nsl.domain.vo.ResultVo;
+import com.se.nsl.mapper.SimuMapper;
+import com.se.nsl.utils.HttpRequestUtil;
+import com.se.nsl.utils.TimeFormatUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@EnableAsync
+public class RealTimeSimulationAsyncService {
+
+    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+    public static final String JSON_EXT = ".json";
+    public static final String DEM_TIF = "DEM.tif";
+    public static final String RESULT_ZARR = "result.zarr";
+    @Resource
+    PropertiesConfig config;
+    @Resource
+    TestService testService;
+    @Resource
+    ResolveService resolveService;
+    @Resource
+    SimuMapper simuMapper;
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    @Resource
+    private RealTimeSimulationConfig rtsConfig;
+    private static final long MILLIS_OF_ONE_DAY = 86400000;
+
+    // 瀛樺偍浠诲姟鎵ц鐘舵��
+    private final Map<Integer, TaskStatus> taskStatusMap = new ConcurrentHashMap<>();
+    // 瀛樺偍浠诲姟鎵ц鐨凢uture瀵硅薄锛岀敤浜庡彇娑堜换鍔�
+    private final Map<String, Future<?>> taskFutureMap = new ConcurrentHashMap<>();
+//    @Autowired
+//    private ThreadPoolTaskExecutor taskExecutor;
+
+    @Async("realTimeExecutor")
+    public void startSimulation(Simu simu) throws IOException {
+        int taskId = simu.getId().intValue();
+        // 妫�鏌ヤ换鍔℃槸鍚﹀凡瀛樺湪
+        if (taskStatusMap.containsKey(taskId) &&
+                taskStatusMap.get(taskId).getStatus() == TaskStatus.Status.RUNNING) {
+            throw new IllegalStateException("Task " + taskId + " is already running");
+        }
+
+        // 鍒濆鍖栦换鍔$姸鎬�
+        TaskStatus status = new TaskStatus();
+        status.setTaskId(taskId);
+        status.setStatus(TaskStatus.Status.RUNNING);
+        taskStatusMap.put(taskId, status);
+
+        updateTaskStatus(simu, "杩愯涓�");
+
+        boolean shouldRun = true;
+        while(shouldRun) {
+            InputStream stream = RealTimeSimulationAsyncService.class.getResourceAsStream("/device_info.json");
+            List<DeviceInfo> deviceInfos = mapper.readValue(stream,
+                    mapper.getTypeFactory().constructCollectionType(List.class, DeviceInfo.class));
+            String serviceName = simu.getServiceName();
+            long currentTime = getCurrentTime();
+            if (serviceName == null) { //娌℃湁杩涜杩囨ā鎷熻绠�
+                serviceName = TimeFormatUtil.formatTime(currentTime, "yyyyMMddHHmmss");
+                simu.setServiceName(serviceName);
+                File serviceNameDir = new File(config.getInPath(), serviceName);
+                serviceNameDir.mkdir();
+                executeSimulateTask(simu, deviceInfos, serviceNameDir, currentTime, serviceName, true);
+                simuMapper.updates(Collections.singletonList(simu));
+            } else {
+                File serviceNameDir = new File(config.getInPath(), serviceName);
+                executeSimulateTask(simu, deviceInfos, serviceNameDir, currentTime, serviceName, false);
+            }
+            try {
+                Thread.sleep(TimeUnit.MINUTES.toMillis(5)); //鏆傚仠5鍒嗛挓
+            } catch (InterruptedException e) {
+                log.error("executing real time simulation exception:", e);
+            }
+            if (taskStatusMap.get(taskId).getStatus() == TaskStatus.Status.CANCELLED) {
+                shouldRun = false;
+            }
+        }
+        updateTaskStatus(simu, "宸插仠姝�");
+        log.info("id涓簕}鐨勬ā鎷熶换鍔″凡鍋滄", taskId);
+    }
+
+    //鏇存柊妯℃嫙浠诲姟鐨勮繍琛岀姸鎬佸埌鏁版嵁搴�
+    private void updateTaskStatus(Simu simu, String result) {
+        simu.setResult(result);
+        simuMapper.updateResult(simu);
+    }
+
+    public void stopSimulation(int taskId) {
+        TaskStatus status = taskStatusMap.get(taskId);
+        if (status == null) {
+            throw new IllegalArgumentException("鏈壘鍒癷d涓�" + taskId + "妯℃嫙浠诲姟");
+        }
+
+        if (status.getStatus() == TaskStatus.Status.RUNNING) {
+            // 鏍囪浠诲姟涓哄凡鍙栨秷
+            status.setCancelled(true);
+            status.setStatus(TaskStatus.Status.CANCELLED);
+        }
+    }
+
+    private long getCurrentTime() {
+        double offsetDays = rtsConfig.getOffsetDays();
+        long millis = (long) (offsetDays * MILLIS_OF_ONE_DAY);
+        return System.currentTimeMillis() - millis;
+    }
+
+    private String executeSimulateTask(Simu simu, List<DeviceInfo> deviceInfos, File serviceNameDir,
+                                       long currentTime, String serviceName, boolean firstTime) throws IOException {
+        SimuData simuData = mapper.readValue(simu.getData(), SimuData.class);
+        simuData.setOutPath(serviceName);
+        simuData.setInPath(serviceName);
+        List<RainGauge> gauges = simuData.getGauges(); //闆ㄩ噺璁′俊鎭�
+        if (firstTime) {
+            resolveService.initArgs(simu, simuData);
+        }
+        //鏍规嵁闆ㄩ噺璁¤鍙栦綑閲忔暟鎹�
+        File newDatFile = generateNewRainfallFile(gauges, deviceInfos, serviceNameDir, currentTime);
+        //鐢熸垚涓�涓柊鐨勭敓鎴恴arr鐨勯厤缃枃浠�
+        ConfigVo configVo;
+        if (firstTime) {
+            configVo = firstTimeZarrConfigFile(serviceNameDir, currentTime, newDatFile);
+        } else {
+            configVo = generateNewZarrConfigFile(serviceNameDir, serviceName, currentTime, newDatFile);
+        }
+        File newConfigFile = new File(serviceNameDir, serviceName + JSON_EXT);
+        mapper.writeValue(newConfigFile, configVo);
+        //鎵ц姹傝В鍣ㄨ繍绠�
+        String cmd = String.format("%s \"%s\"", config.getUwSolverBat(), newConfigFile);
+        callBat(cmd);
+
+        File newZarr2TifJson;
+        if (firstTime) {
+            newZarr2TifJson = generateNewZarr2TifJson(serviceNameDir, currentTime, Collections.emptyList());
+        } else {
+            //鐢熸垚涓�涓柊鐨剒arr杞瑃if鐨刯son鏂囦欢
+            ResultVo result = configVo.getResult();
+            Integer fromFrame = result.getLastFrames();
+            Integer toFrame = result.getSave_frames() - 1;
+            newZarr2TifJson = generateNewZarr2TifJson(serviceNameDir, currentTime, Arrays.asList(fromFrame, toFrame));
+        }
+        //鎵цzarr杞瑃if
+        String zarr2TifCmd = String.format("%s \"%s\"", config.getZarr2tifBat(), newZarr2TifJson);
+        callBat(zarr2TifCmd);
+        //杩斿洖鏂扮殑layer.json鍚嶇О
+        if (firstTime) {
+            return generateLayerJsonAndPng(serviceName, serviceNameDir, -1);
+        } else {
+            return generateLayerJsonAndPng(serviceName, serviceNameDir, currentTime);
+        }
+    }
+
+    private String generateLayerJsonAndPng(String serviceName, File serviceNameDir,
+                                           long currentTime) throws IOException {
+        ResultDto resultDto = new ResultDto();
+        resultDto.setServiceName(serviceName);
+        File temp = Paths.get(config.getOutPath(), serviceName, "temp").toFile();
+        resultDto.setTemp(temp.getAbsolutePath());
+        resultDto.setOutPath(Paths.get(config.getOutPath(), serviceName).toString());
+        File dem = new File(serviceNameDir, DEM_TIF);
+        resultDto.setTerrainFile(dem.getAbsolutePath());
+        File newDepthDir;
+        if (currentTime < 0) {
+            newDepthDir = new File(serviceNameDir + File.separator + "depth");
+        } else {
+            newDepthDir = new File(serviceNameDir + File.separator + "depth_" + currentTime);
+        }
+        resultDto.setWaterPath(newDepthDir.getAbsolutePath());
+        LayerDto layerDto = new LayerDto(config.getVer(), config.getEpsg(), config.getSizes());
+        String newLayerJsonName;
+        if (currentTime < 0) {
+            newLayerJsonName = "layer.json";
+        } else {
+            newLayerJsonName = "layer_" + currentTime + JSON_EXT;
+        }
+        layerDto.setName(newLayerJsonName);
+        testService.processRealTime(resultDto, layerDto);
+        log.info("瀹炴椂妯℃嫙瀹屾垚");
+        return newLayerJsonName;
+    }
+
+    private File generateNewZarr2TifJson(File serviceNameDir, long currentTime, List<Object> range) throws IOException {
+        Zarr2Tif zarr2Tif;
+        File newZarr2TifJson;
+        String newZarrPath;
+        String geotiffDir;
+        if (range.isEmpty()) {
+            zarr2Tif = new Zarr2Tif();
+            newZarrPath = serviceNameDir + File.separator + RESULT_ZARR;
+            geotiffDir = serviceNameDir + File.separator + "depth";
+            zarr2Tif.setTerrain_file(serviceNameDir + File.separator + DEM_TIF);
+            newZarr2TifJson = new File(serviceNameDir, "zarr2tif.json");
+        } else {
+            File srcZarr2TifJson = new File(serviceNameDir, "zarr2tif.json");
+            zarr2Tif = mapper.readValue(srcZarr2TifJson, Zarr2Tif.class);
+            //淇敼zarr2tif瀵硅薄涓殑瀛楁
+            newZarrPath = serviceNameDir + File.separator + RESULT_ZARR;
+            geotiffDir = serviceNameDir + File.separator + "depth_" + currentTime;
+            newZarr2TifJson = new File(serviceNameDir, "zarr2tif_" + currentTime + JSON_EXT);
+        }
+        zarr2Tif.setGeotiff_dir(geotiffDir);
+        zarr2Tif.setZarr_file(newZarrPath);
+        zarr2Tif.setRange(range);
+        mapper.writeValue(newZarr2TifJson, zarr2Tif);
+        return newZarr2TifJson;
+    }
+
+    private ConfigVo firstTimeZarrConfigFile(File serviceNameDir, long currentTime, File newDatFile) {
+        ConfigVo configVo = new ConfigVo();
+        String serviceNameDirPath = serviceNameDir.getAbsolutePath();
+        configVo.setTerrain(serviceNameDirPath + File.separator + DEM_TIF);
+        configVo.setLanduse(serviceNameDirPath + File.separator + "Landuse.tif");
+        File stationFile = new File(serviceNameDir, "Station.tif");
+        if (stationFile.exists()) {
+            configVo.setStation(stationFile.getAbsolutePath());
+        }
+        int realTimeSimulateTime = 300; //妯℃嫙鏃堕棿锛岄粯璁や负5min锛屽嵆300s
+        int realTimeInterval = rtsConfig.getRealTimeInterval(); //姣忓抚鐨勯棿闅旀椂闂�
+        List<String> rainGauge = new ArrayList<>();
+        rainGauge.add(newDatFile.getAbsolutePath());//raingage file
+        rainGauge.add("mm/min");
+        configVo.setRaingage(rainGauge);
+        String saveName = serviceNameDir + File.separator + RESULT_ZARR;
+        ResultVo result = new ResultVo(saveName, realTimeSimulateTime / realTimeInterval,
+                realTimeInterval, "continue", null);
+        result.setSave_interval(realTimeInterval);
+        result.setSave_timestamp0(TimeFormatUtil.formatTime(currentTime, YYYY_MM_DD_HH_MM_SS));
+        configVo.setDuration(realTimeSimulateTime);
+        result.setSave_name(saveName);
+        result.setSave_filter(config.getSaveFilter());
+        configVo.setResult(result);
+        return configVo;
+    }
+
+    private ConfigVo generateNewZarrConfigFile(File serviceNameDir, String serviceName, long currentTime, File newDatFile) throws IOException {
+        File configFile = new File(serviceNameDir, serviceName + JSON_EXT);
+        ConfigVo configVo = mapper.readValue(configFile, ConfigVo.class);
+        File stationFile = new File(serviceNameDir, "Station.tif");
+        if (stationFile.exists()) {
+            configVo.setStation(stationFile.getAbsolutePath());
+        }
+        int realTimeSimulateTime = 300; //妯℃嫙鏃堕棿锛岄粯璁や负5min锛屽嵆300s
+        int realTimeInterval = rtsConfig.getRealTimeInterval(); //姣忓抚鐨勯棿闅旀椂闂�
+        configVo.getRaingage().set(0, newDatFile.getAbsolutePath()); //raingage file
+        ResultVo result = configVo.getResult();
+        Integer lastFrames = result.getLastFrames();
+        if (lastFrames == null) {
+            lastFrames = 0;
+        }
+        Integer saveStart = result.getSave_start();
+        Integer saveInterval = result.getSave_interval();
+        Integer saveFrames = result.getSave_frames();
+        int newStartPoint = saveStart + (saveFrames - lastFrames) * saveInterval;
+        int newSaveFrames = saveFrames + (realTimeSimulateTime / realTimeInterval);
+        result.setSave_start(newStartPoint); //璧峰鏃堕棿瑕佸湪涓婃鏃堕棿鐨勫熀纭�涓婂紑濮�
+        result.setLastFrames(saveFrames);
+        result.setSave_frames(newSaveFrames); //淇濈暀5甯э紝鍦ㄥ師鏉ョ殑鍩虹涓婂鍔�5甯�
+        result.setSave_timestamp0(TimeFormatUtil.formatTime(currentTime, YYYY_MM_DD_HH_MM_SS));
+        configVo.setDuration(newStartPoint + realTimeSimulateTime);
+        return configVo;
+    }
+
+    private File generateNewRainfallFile(List<RainGauge> gauges, List<DeviceInfo> deviceInfos,
+                                         File serviceNameDir, long currentTime) throws IOException {
+        String title = config.getRainfallTitle();
+        List<String> newLines = new ArrayList<>();
+        newLines.add(title);
+        Map<String, Integer> deviceInfoMap = deviceInfos.stream().collect(Collectors.toMap(DeviceInfo::getId, DeviceInfo::getMappingId));
+        List<RainRecord> rainRecords = new ArrayList<>();
+        for (RainGauge gauge : gauges) {
+            String id = gauge.getId();
+            Integer mappingId = deviceInfoMap.getOrDefault(id, 0);
+            RainRecord rr = getRainGaugeResult(gauge, mappingId, currentTime);
+            rainRecords.add(rr);
+        }
+        fillEmptyValue(rainRecords); //濉厖娌℃湁鍊肩殑闆ㄩ噺璁�
+        for (RainRecord rr : rainRecords) {
+            newLines.add(rr.toString());
+        }
+        File newDatFile = new File(serviceNameDir, "rainfall_" + currentTime + ".dat");
+        if (!newDatFile.exists()) newDatFile.createNewFile();
+        Files.write(newDatFile.toPath(), newLines, StandardOpenOption.TRUNCATE_EXISTING);
+        return newDatFile;
+    }
+
+    //灏嗛洦閲忚涓┖鍊硷紙intensity涓�-1锛夌殑閮ㄥ垎濉厖鍊�
+    private void fillEmptyValue(List<RainRecord> rainRecords) {
+        double[] rainValues = rainRecords.stream().mapToDouble(r -> r.getIntensity()).toArray();
+        double[] forwardValues = forwardFill(rainValues);
+        double[] backwordValues = backwordFill(rainValues);
+        for (int i = 0; i < rainRecords.size(); i++) {
+            RainRecord rr = rainRecords.get(i);
+            double filledValue = forwardValues[i] != -1 ? forwardValues[i] : backwordValues[i];
+            rr.setIntensity(filledValue);
+        }
+        //濡傛灉閮芥槸-1锛屽垯璁剧疆涓�0
+        for (RainRecord rr : rainRecords) {
+            double intensity = rr.getIntensity();
+            if (intensity == -1) {
+                rr.setIntensity(0);
+            }
+        }
+    }
+
+    private double[] forwardFill(double[] rainValues) {
+        double[] result = Arrays.copyOf(rainValues, rainValues.length);
+        double lastValid = -1D;
+        for (int i = 0; i < result.length; i++) {
+            if (result[i] != -1) {
+                lastValid = result[i];
+            } else if (lastValid != -1) {
+                result[i] = lastValid;
+            }
+        }
+        return result;
+    }
+
+    private double[] backwordFill(double[] rainValues) {
+        double[] result = Arrays.copyOf(rainValues, rainValues.length);
+        double lastValid = -1D;
+        for (int i = result.length - 1; i >= 0; i--) {
+            if (result[i] != -1) {
+                lastValid = result[i];
+            } else if (lastValid != -1) {
+                result[i] = lastValid;
+            }
+        }
+        return result;
+    }
+
+    private RainRecord getRainGaugeResult(RainGauge gauge, int stationId, long currentTime) throws JsonProcessingException {
+        double lon = gauge.getX();
+        double lat = gauge.getY();
+        LocalDateTime dateTime = TimeFormatUtil.toDate(currentTime);
+        int year = dateTime.getYear();
+        int month = dateTime.getMonthValue();
+        int day = dateTime.getDayOfMonth();
+        int hour = dateTime.getHour();
+        int minute = dateTime.getMinute();
+        String id = gauge.getId();
+        long someMinutesAgo = currentTime - TimeUnit.MINUTES.toMillis(rtsConfig.getRequestOffsetMinutes());
+        String startTime = TimeFormatUtil.formatTime(someMinutesAgo, YYYY_MM_DD_HH_MM_SS);
+        String endTime = TimeFormatUtil.formatTime(currentTime, YYYY_MM_DD_HH_MM_SS);
+        double intensity = getIntensityByDeviceId(id, startTime, endTime); //淇濈暀鎸囧畾浣嶆暟灏忔暟
+//        return String.format("%s %s %s %s %s %s %s %s %s",
+//                stationId, lon, lat, year, month, day, hour, minute, String.format("%.6f", intensity));
+        return new RainRecord(stationId, lon, lat, year, month, day, hour, minute, intensity);
+    }
+
+    //鏍规嵁闆ㄩ噺璁℃爣璇嗘煡璇㈤洦閲忔暟鎹�
+    private double getIntensityByDeviceId(String deviceId, String startTime, String endTime) throws JsonProcessingException {
+        RemoteGaugeInput input = new RemoteGaugeInput();
+        input.setCurrentPage(1);
+        input.setPageSize(2);
+        RemoteGaugeInput.FilterObject filterObject = new RemoteGaugeInput.FilterObject();
+        filterObject.setDeviceCode(deviceId);
+        filterObject.setSendTimeList(Arrays.asList(startTime, endTime));
+        input.setFilterObject(filterObject);
+        String url = rtsConfig.getUrl();
+        String token = rtsConfig.getToken();
+        ResponseEntity<String> post = HttpRequestUtil.post(url, input, String.class, token);
+        String body = post.getBody();
+        JsonNode jsonNode = mapper.readTree(body);
+        JsonNode pageData = jsonNode.get("data").get("pageData");
+        if (!pageData.isEmpty()) {
+            return pageData.get(0).get("value").asDouble();
+        }
+        return -1D;
+    }
+
+    private String callBat(String cmd) {
+        try {
+            ProcessBuilder pb = new ProcessBuilder("cmd", "/c", cmd);
+            pb.redirectErrorStream(true); // 鍚堝苟閿欒娴佸埌鏍囧噯杈撳嚭
+            Process process = pb.start();
+            process.getOutputStream().close();
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    log.info(line);
+                }
+            }
+            int exitCode = process.waitFor();
+            return "" + exitCode; // sb.toString();
+        } catch (Exception ex) {
+            log.error(ex.getMessage(), ex);
+            return null;
+        }
+    }
+
+    static class RainRecord {
+        private int stationId;
+        private double lon;
+        private double lat;
+        private int year;
+        private int month;
+        private int day;
+        private int hour;
+        private int minute;
+        private double intensity; //-1浠h〃娌℃湁鏁板��
+
+        public RainRecord() {
+        }
+
+        public RainRecord(int stationId, double lon, double lat, int year, int month,
+                          int day, int hour, int minute, double intensity) {
+            this.stationId = stationId;
+            this.lon = lon;
+            this.lat = lat;
+            this.year = year;
+            this.month = month;
+            this.day = day;
+            this.hour = hour;
+            this.minute = minute;
+            this.intensity = intensity;
+        }
+
+        public int getStationId() {
+            return stationId;
+        }
+
+        public void setStationId(int stationId) {
+            this.stationId = stationId;
+        }
+
+        public double getLon() {
+            return lon;
+        }
+
+        public void setLon(double lon) {
+            this.lon = lon;
+        }
+
+        public double getLat() {
+            return lat;
+        }
+
+        public void setLat(double lat) {
+            this.lat = lat;
+        }
+
+        public int getYear() {
+            return year;
+        }
+
+        public void setYear(int year) {
+            this.year = year;
+        }
+
+        public int getMonth() {
+            return month;
+        }
+
+        public void setMonth(int month) {
+            this.month = month;
+        }
+
+        public int getDay() {
+            return day;
+        }
+
+        public void setDay(int day) {
+            this.day = day;
+        }
+
+        public int getHour() {
+            return hour;
+        }
+
+        public void setHour(int hour) {
+            this.hour = hour;
+        }
+
+        public int getMinute() {
+            return minute;
+        }
+
+        public void setMinute(int minute) {
+            this.minute = minute;
+        }
+
+        public double getIntensity() {
+            return intensity;
+        }
+
+        public void setIntensity(double intensity) {
+            this.intensity = intensity;
+        }
+
+        @Override
+        public String toString() {
+            return "" + stationId + " " + lon + " " + lat +
+                    " " + year + " " + month + " " + day +
+                    " " + hour + " " + minute + " " + String.format("%.6f", intensity);
+        }
+    }
+
+    // 浠诲姟鐘舵�佸唴閮ㄧ被
+    static class TaskStatus {
+        public enum Status {
+            RUNNING, COMPLETED, FAILED, CANCELLED
+        }
+
+        private int taskId;
+        private Status status;
+        private boolean cancelled;
+        private String errorMessage;
+
+        public int getTaskId() {
+            return taskId;
+        }
+
+        public void setTaskId(int taskId) {
+            this.taskId = taskId;
+        }
+
+        public Status getStatus() {
+            return status;
+        }
+
+        public void setStatus(Status status) {
+            this.status = status;
+        }
+
+        public boolean isCancelled() {
+            return cancelled;
+        }
+
+        public void setCancelled(boolean cancelled) {
+            this.cancelled = cancelled;
+        }
+
+        public String getErrorMessage() {
+            return errorMessage;
+        }
+
+        public void setErrorMessage(String errorMessage) {
+            this.errorMessage = errorMessage;
+        }
+    }
+
+}
diff --git a/src/main/java/com/se/nsl/service/RealTimeSimulationService.java b/src/main/java/com/se/nsl/service/RealTimeSimulationService.java
index 9c1622b..d31d879 100644
--- a/src/main/java/com/se/nsl/service/RealTimeSimulationService.java
+++ b/src/main/java/com/se/nsl/service/RealTimeSimulationService.java
@@ -50,7 +50,7 @@
     private RealTimeSimulationConfig rtsConfig;
     private static final long MILLIS_OF_ONE_DAY = 86400000;
 
-    public String realTimeSimulate(Simu simu) throws IOException {
+    public String startSimulation(Simu simu) throws IOException {
         InputStream stream = RealTimeSimulationService.class.getResourceAsStream("/device_info.json");
         List<DeviceInfo> deviceInfos = mapper.readValue(stream,
                 mapper.getTypeFactory().constructCollectionType(List.class, DeviceInfo.class));
@@ -181,6 +181,7 @@
         String serviceNameDirPath = serviceNameDir.getAbsolutePath();
         configVo.setTerrain(serviceNameDirPath + File.separator + DEM_TIF);
         configVo.setLanduse(serviceNameDirPath + File.separator + "Landuse.tif");
+        configVo.setEvaporation(config.getEvaporation());
         File stationFile = new File(serviceNameDir, "Station.tif");
         if (stationFile.exists()) {
             configVo.setStation(stationFile.getAbsolutePath());
diff --git a/src/main/java/com/se/nsl/service/ResolveService.java b/src/main/java/com/se/nsl/service/ResolveService.java
index a78e688..01b4c9f 100644
--- a/src/main/java/com/se/nsl/service/ResolveService.java
+++ b/src/main/java/com/se/nsl/service/ResolveService.java
@@ -342,6 +342,7 @@
         String startTime = TimeFormatUtil.formatDate(data.getStartTime());
         ConfigVo vo = new ConfigVo(terrainFile, landuseFile, terrainFile, rainfallFile,
                 saveName, duration, saveFrames, saveMode, startTime);
+        vo.setEvaporation(config.getEvaporation());
         vo.getResult().setSave_filter(config.getSaveFilter());
         String configFile = config.getInPath() + File.separator + data.getInPath() + File.separator + data.getInPath() + ".json";
 //        ComHelper.writeJson(configFile, JSON.toJSONString(vo));
@@ -517,115 +518,17 @@
         Files.write(Paths.get(dat), list, StandardCharsets.UTF_8);
     }
 
-    //瀹炴椂妯℃嫙
-//    public String realTimeSimulate(RealTimeInput input) throws IOException {
-//        long currentTime = System.currentTimeMillis();
-//        //鏍规嵁鏈嶅姟鎵惧埌鎸囧畾鐨勬枃浠跺す
-//        String serviceName = input.getServiceName();
-//        File serviceNameDir = new File(config.getInPath(), serviceName);
-//        //鐢熸垚涓�涓柊鐨勯洦閲忔枃浠�,闇�瑕佸師鍏堥洦閲忔枃浠剁殑涓�浜涗俊鎭紝鎵�浠ラ渶瑕佸厛璇诲彇鏃х殑
-//        String[] values = readTheOldFirstLineRainfallValue(serviceNameDir);
-//        File newDatFile = generateNewRainfallFile(input, values, serviceNameDir, currentTime);
-//
-//        //鐢熸垚涓�涓柊鐨勭敓鎴恴arr鐨勯厤缃枃浠�
-//        File newConfigFile = generateNewZarrConfigFile(serviceNameDir, serviceName, currentTime, newDatFile);
-//        //鎵ц姹傝В鍣ㄨ繍绠�
-//        String cmd = String.format("%s \"%s\"", config.getUwSolverBat(), newConfigFile);
-//        callBat2(cmd);
-//
-//        //鐢熸垚涓�涓柊鐨剒arr杞瑃if鐨刯son鏂囦欢
-//        File newZarr2TifJson = generateNewZarr2TifJson(serviceNameDir, currentTime);
-//        //鎵цzarr杞瑃if
-//        String zarr2TifCmd = String.format("%s \"%s\"", config.getZarr2tifBat(), newZarr2TifJson);
-//        callBat2(zarr2TifCmd);
-//        //杩斿洖鏂扮殑layer.json鍚嶇О
-//        return generateLayerJsonAndPng(serviceName, serviceNameDir, currentTime);
-//
-//    }
-
-//    private String generateLayerJsonAndPng(String serviceName, File serviceNameDir, long currentTime) throws IOException {
-//        ResultDto resultDto = new ResultDto();
-//        resultDto.setServiceName(serviceName);
-//        File temp = Paths.get(config.getOutPath(), serviceName, "temp").toFile();
-//        if (!temp.exists()) temp.mkdir();
-//        resultDto.setTemp(temp.getAbsolutePath());
-//        resultDto.setOutPath(Paths.get(config.getOutPath(), serviceName).toString());
-//        File dem = new File(serviceNameDir, "DEM.tif");
-//        resultDto.setTerrainFile(dem.getAbsolutePath());
-//        File newDepthDir = new File(serviceNameDir + File.separator + "depth_" + currentTime);
-//        resultDto.setWaterPath(newDepthDir.getAbsolutePath());
-//        LayerDto layerDto = new LayerDto(config.getVer(), 4548, config.getSizes());
-//        String newLayerJsonName = "layer_" + currentTime + ".json";
-//        layerDto.setName(newLayerJsonName);
-//        testService.processRealTime(resultDto, layerDto);
-//        return newLayerJsonName;
-//    }
-
-//    private File generateNewZarr2TifJson(File serviceNameDir, long currentTime) throws IOException {
-//        File srcZarr2TifJson = new File(serviceNameDir, "zarr2tif.json");
-//        Zarr2Tif zarr2Tif = mapper.readValue(srcZarr2TifJson, Zarr2Tif.class);
-//        //淇敼zarr2tif瀵硅薄涓殑瀛楁
-////        String stamp = TimeFormatUtil.formatTime(currentTime, "yyyy-MM-dd HH:mm:ss");
-////        zarr2Tif.setStart_timestamp(stamp);
-////        String newZarrPath = serviceNameDir + File.separator + "result_" + currentTime + ".zarr";
-//        String newZarrPath = serviceNameDir + File.separator + "result.zarr";
-//        zarr2Tif.setZarr_file(newZarrPath);
-//        zarr2Tif.setGeotiff_dir(serviceNameDir + File.separator + "depth_" + currentTime);
-//        File newZarr2TifJson = new File(serviceNameDir, "zarr2tif_" + currentTime + ".json");
-//        mapper.writeValue(newZarr2TifJson, zarr2Tif);
-//        return newZarr2TifJson;
-//    }
-
-//    private File generateNewZarrConfigFile(File serviceNameDir, String serviceName, long currentTime, File newDatFile) throws IOException {
-//        File configFile = new File(serviceNameDir, serviceName + ".json");
-//        ConfigVo configVo = mapper.readValue(configFile, ConfigVo.class);
-//        int simulateTime = 300; //妯℃嫙鏃堕棿锛岄粯璁や负5min锛屽嵆300s
-//        int intervalTime = 60; //姣忓抚鐨勯棿闅旀椂闂达紝榛樿涓�60s,60s鐢熸垚涓�甯�
-//        configVo.getRaingage().set(0, newDatFile.getAbsolutePath()); //raingage file
-//        ResultVo result = configVo.getResult();
-//        Integer oldDuration = configVo.getDuration();
-////        result.setSave_start(oldDuration); //璧峰鏃堕棿瑕佸湪涓婃鏃堕棿鐨勫熀纭�涓婂紑濮�
-//        configVo.setDuration(oldDuration + simulateTime); //鍥哄畾涓�5min
-//        result.setSave_interval(intervalTime);
-//        result.setSave_frames(result.getSave_frames() + (simulateTime / intervalTime)); //淇濈暀5甯э紝鍦ㄥ師鏉ョ殑鍩虹涓婂鍔�5甯�
-////        String newZarrPath = serviceNameDir + File.separator + "result_" + currentTime + ".zarr";
-//        String newZarrPath = serviceNameDir + File.separator + "result.zarr";
-//        result.setSave_name(newZarrPath);
-//        File newConfigFile = new File(serviceNameDir, currentTime + ".json");
-//        mapper.writeValue(newConfigFile, configVo);
-//        return newConfigFile;
-//    }
-
-//    private File generateNewRainfallFile(RealTimeInput input, String[] values, File serviceNameDir, long currentTime) throws IOException {
-//        String station = values[0];
-//        double lon = Double.parseDouble(values[1]);
-//        double lat = Double.parseDouble(values[2]);
-//        String title = config.getRainfallTitle();
-//        List<String> newLines = new ArrayList<>();
-//        newLines.add(title);
-//        List<RealTimeInput.RealTimeData> data = input.getData();
-//        for (RealTimeInput.RealTimeData rd : data) {
-//            LocalDateTime dateTime = rd.getDateTime();
-//            int year = dateTime.getYear();
-//            int month = dateTime.getMonthValue();
-//            int day = dateTime.getDayOfMonth();
-//            int hour = dateTime.getHour();
-//            int minute = dateTime.getMinute();
-//            double intensity = rd.getIntensity(); //淇濈暀鎸囧畾浣嶆暟灏忔暟
-//            String l = String.format("%s %s %s %s %s %s %s %s %s",
-//                    station, lon, lat, year, month, day, hour, minute, String.format("%.6f", intensity));
-//            newLines.add(l);
-//        }
-//        File newDatFile = new File(serviceNameDir, "rainfall_" + currentTime + ".dat");
-//        if (!newDatFile.exists()) newDatFile.createNewFile();
-//        Files.write(newDatFile.toPath(), newLines, StandardOpenOption.TRUNCATE_EXISTING);
-//        return newDatFile;
-//    }
-
-//    private static String[] readTheOldFirstLineRainfallValue(File serviceNameDir) throws IOException {
-//        File srcRailfallFile = new File(serviceNameDir, "rainfall.dat");
-//        List<String> lines = Files.readAllLines(srcRailfallFile.toPath());
-//        String secondLine = lines.get(1);
-//        return secondLine.split(" ");
-//    }
+    public List<String> simulationResults(String serviceName) {
+        String outPath = config.getOutPath();
+        File serviceNameDir = new File(outPath, serviceName);
+        List<String> res = new ArrayList<>();
+        File[] files = serviceNameDir.listFiles();
+        for (File file : files) {
+            String name = file.getName();
+            if (name.startsWith("layer")) {
+                res.add(name);
+            }
+        }
+        return res;
+    }
 }
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 5e119e7..4212d20 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -152,6 +152,7 @@
   #鐢熸垚甯ф暟鐨勯棿闅旀椂闂达紝鍗曚綅鏄垎閽燂紝璁剧疆涓�5琛ㄧず姣忛殧5鍒嗛挓鐢熸垚涓�甯�
   saveFrameInterval: 20
   saveFilter: 0.015
+  evaporation: 0.27
   # 鍦熷湴鍒╃敤锛�1-Cropland,2-Forest,3-Shrub,4-Grassland,5-Water,6-Snow/Ice,7-Barren,8-Impervious,9-Wetland
   landuse: 2
   #sizes: 64,128,256,512,1024,2048,4096
diff --git a/src/main/resources/mapper/SimuMapper.xml b/src/main/resources/mapper/SimuMapper.xml
index b9eca9e..d86d159 100644
--- a/src/main/resources/mapper/SimuMapper.xml
+++ b/src/main/resources/mapper/SimuMapper.xml
@@ -83,4 +83,8 @@
             where id = #{item.id}
         </foreach>
     </update>
+
+    <update id="updateResult" parameterType="com.se.nsl.domain.po.Simu">
+        update nsl.tbl_yj_tr_simulate set result=#{result} where id = #{id}
+    </update>
 </mapper>
\ No newline at end of file

--
Gitblit v1.9.3