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.RealTimeSimulationConfig;
|
import com.se.nsl.config.PropertiesConfig;
|
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.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.TimeUnit;
|
import java.util.stream.Collectors;
|
|
@Slf4j
|
@Service
|
public class RealTimeSimulationService {
|
|
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;
|
|
public String realTimeSimulate(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));
|
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();
|
String layerJsonName = executeSimulateTask(simu, deviceInfos, serviceNameDir, currentTime, serviceName, true);
|
simuMapper.updates(Collections.singletonList(simu));
|
return layerJsonName;
|
} else {
|
File serviceNameDir = new File(config.getInPath(), serviceName);
|
return executeSimulateTask(simu, deviceInfos, serviceNameDir, currentTime, serviceName ,false);
|
}
|
}
|
|
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);
|
//生成一个新的生成zarr的配置文件
|
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 {
|
//生成一个新的zarr转tif的json文件
|
ResultVo result = configVo.getResult();
|
Integer fromFrame = result.getLastFrames();
|
Integer toFrame = result.getSave_frames() - 1;
|
newZarr2TifJson = generateNewZarr2TifJson(serviceNameDir, currentTime, Arrays.asList(fromFrame, toFrame));
|
}
|
//执行zarr转tif
|
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));
|
for (RainGauge gauge : gauges) {
|
String id = gauge.getId();
|
Integer mappingId = deviceInfoMap.getOrDefault(id, 0);
|
String l = getRainGaugeResult(gauge, mappingId, currentTime);
|
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 String 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));
|
}
|
|
//根据雨量计标识查询雨量数据
|
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 0;
|
}
|
|
|
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;
|
}
|
}
|
|
}
|