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