设备、临界数据根据设备类型做导入

This commit is contained in:
wanxiaoli 2025-12-25 09:16:29 +08:00
parent 8c277528cb
commit b8b122980f
12 changed files with 222 additions and 123 deletions

View File

@ -1,23 +1,30 @@
package com.yfd.business.css.config; package com.yfd.business.css.config;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import jakarta.annotation.Resource;
import javax.sql.DataSource; import javax.sql.DataSource;
@Configuration @Configuration
public class MybatisConfig { public class MybatisConfig {
@Resource
private MybatisPlusInterceptor mybatisPlusInterceptor;
@Bean @Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean(); MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
factoryBean.setDataSource(dataSource); factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:/mapper/**/*.xml")); .getResources("classpath*:/mapper/**/*.xml"));
factoryBean.setPlugins(new Interceptor[]{ mybatisPlusInterceptor });
return factoryBean.getObject(); return factoryBean.getObject();
} }

View File

@ -66,20 +66,13 @@ public class AlgorithmController {
public Page<Algorithm> searchAlgorithms(@RequestParam(required = false) String name, public Page<Algorithm> searchAlgorithms(@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Algorithm> qw = new QueryWrapper<>(); QueryWrapper<Algorithm> qw = new QueryWrapper<>();
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
qw.like("name", name); qw.like("name", name);
} }
qw.orderByDesc("updated_at").last("LIMIT " + offset + "," + pageSize); qw.orderByDesc("updated_at");
java.util.List<Algorithm> records = algorithmService.list(qw);
long total = (name != null && !name.isEmpty())
? algorithmService.count(new QueryWrapper<Algorithm>().like("name", name))
: algorithmService.count();
Page<Algorithm> page = new Page<>(pageNum, pageSize, true); Page<Algorithm> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records); return algorithmService.page(page, qw);
page.setTotal(total);
return page;
} }
private String currentUsername() { private String currentUsername() {

View File

@ -76,8 +76,9 @@ public class CriticalDataController {
* @return 是否导入成功 * @return 是否导入成功
*/ */
@PostMapping("/import") @PostMapping("/import")
public boolean importCriticalData(@RequestParam("file") MultipartFile file) { public boolean importCriticalData(@RequestParam("file") MultipartFile file,
return criticalDataService.importCriticalData(file); @RequestParam String deviceType) {
return criticalDataService.importCriticalData(file, deviceType);
} }
@ -95,16 +96,10 @@ public class CriticalDataController {
public Page<CriticalData> listByDeviceType(@RequestParam String deviceType, public Page<CriticalData> listByDeviceType(@RequestParam String deviceType,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize); Page<CriticalData> page = new Page<>(pageNum, pageSize, true);
QueryWrapper<CriticalData> qw = new QueryWrapper<CriticalData>() QueryWrapper<CriticalData> qw = new QueryWrapper<CriticalData>()
.eq("device_type", deviceType) .eq("device_type", deviceType)
.orderByDesc("created_at") .orderByDesc("created_at");
.last("LIMIT " + offset + "," + pageSize); return criticalDataService.page(page, qw);
java.util.List<CriticalData> records = criticalDataService.list(qw);
long total = criticalDataService.count(new QueryWrapper<CriticalData>().eq("device_type", deviceType));
Page<CriticalData> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records);
page.setTotal(total);
return page;
} }
} }

View File

@ -85,8 +85,9 @@ public class DeviceController {
* @return 是否导入成功 * @return 是否导入成功
*/ */
@PostMapping("/import") @PostMapping("/import")
public boolean importDevices(@RequestParam("file") MultipartFile file) { public boolean importDevices(@RequestParam("file") MultipartFile file,
return deviceService.importDevices(file); @RequestParam String deviceType) {
return deviceService.importDevices(file, deviceType);
} }
@ -124,7 +125,6 @@ public class DeviceController {
@RequestParam(required = false) String name, @RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Device> qw = new QueryWrapper<>(); QueryWrapper<Device> qw = new QueryWrapper<>();
if (type != null && !type.isEmpty()) { if (type != null && !type.isEmpty()) {
qw.eq("type", type); qw.eq("type", type);
@ -132,20 +132,9 @@ public class DeviceController {
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
qw.like("name", name); qw.like("name", name);
} }
qw.orderByDesc("created_at").last("LIMIT " + offset + "," + pageSize); qw.orderByDesc("created_at");
java.util.List<Device> records = deviceService.list(qw);
QueryWrapper<Device> countQw = new QueryWrapper<>();
if (type != null && !type.isEmpty()) {
countQw.eq("type", type);
}
if (name != null && !name.isEmpty()) {
countQw.like("name", name);
}
long total = deviceService.count(countQw);
Page<Device> page = new Page<>(pageNum, pageSize, true); Page<Device> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records); return deviceService.page(page, qw);
page.setTotal(total);
return page;
} }
private String currentUsername() { private String currentUsername() {

View File

@ -102,37 +102,23 @@ public class MaterialController {
public Page<Material> search(@RequestParam(required = false) String name, public Page<Material> search(@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Material> qw = new QueryWrapper<Material>().orderByDesc("created_at"); QueryWrapper<Material> qw = new QueryWrapper<Material>().orderByDesc("created_at");
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
qw.like("name", name); qw.like("name", name);
} }
qw.last("LIMIT " + offset + "," + pageSize);
java.util.List<Material> records = materialService.list(qw);
long total = (name != null && !name.isEmpty())
? materialService.count(new QueryWrapper<Material>().like("name", name))
: materialService.count();
Page<Material> page = new Page<>(pageNum, pageSize, true); Page<Material> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records); return materialService.page(page, qw);
page.setTotal(total);
return page;
} }
@GetMapping("/by-project") @GetMapping("/by-project")
public Page<Material> pageByProject(@RequestParam String projectId, public Page<Material> pageByProject(@RequestParam String projectId,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Material> qw = new QueryWrapper<Material>() QueryWrapper<Material> qw = new QueryWrapper<Material>()
.eq("project_id", projectId) .eq("project_id", projectId)
.orderByDesc("created_at") .orderByDesc("created_at");
.last("LIMIT " + offset + "," + pageSize);
java.util.List<Material> records = materialService.list(qw);
long total = materialService.count(new QueryWrapper<Material>().eq("project_id", projectId));
Page<Material> page = new Page<>(pageNum, pageSize, true); Page<Material> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records); return materialService.page(page, qw);
page.setTotal(total);
return page;
} }
private String currentUsername() { private String currentUsername() {

View File

@ -156,23 +156,12 @@ public class ProjectController {
public Page<Project> search(@RequestParam(required = false) @Parameter(description = "项目名称关键词,可为空") String name, public Page<Project> search(@RequestParam(required = false) @Parameter(description = "项目名称关键词,可为空") String name,
@RequestParam(defaultValue = "1") @Parameter(description = "页码默认1") long pageNum, @RequestParam(defaultValue = "1") @Parameter(description = "页码默认1") long pageNum,
@RequestParam(defaultValue = "20") @Parameter(description = "每页条数默认20") long pageSize) { @RequestParam(defaultValue = "20") @Parameter(description = "每页条数默认20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Project> qw = new QueryWrapper<Project>().orderByDesc("created_at"); QueryWrapper<Project> qw = new QueryWrapper<Project>().orderByDesc("created_at");
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
qw.like("name", name); qw.like("name", name);
} }
qw.last("LIMIT " + offset + "," + pageSize);
java.util.List<Project> records = projectService.list(qw);
long total;
if (name != null && !name.isEmpty()) {
total = projectService.count(new QueryWrapper<Project>().like("name", name));
} else {
total = projectService.count();
}
Page<Project> page = new Page<>(pageNum, pageSize, true); Page<Project> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records); return projectService.page(page, qw);
page.setTotal(total);
return page;
} }
/** /**

View File

@ -103,20 +103,12 @@ public class ScenarioController {
@RequestParam(required = false) String name, @RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "1") long pageNum,
@RequestParam(defaultValue = "20") long pageSize) { @RequestParam(defaultValue = "20") long pageSize) {
long offset = Math.max(0, (pageNum - 1) * pageSize);
QueryWrapper<Scenario> qw = new QueryWrapper<Scenario>().eq("project_id", projectId).orderByDesc("created_at"); QueryWrapper<Scenario> qw = new QueryWrapper<Scenario>().eq("project_id", projectId).orderByDesc("created_at");
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
qw.like("name", name); qw.like("name", name);
} }
qw.last("LIMIT " + offset + "," + pageSize);
java.util.List<Scenario> records = scenarioService.list(qw);
long total = (name != null && !name.isEmpty())
? scenarioService.count(new QueryWrapper<Scenario>().eq("project_id", projectId).like("name", name))
: scenarioService.count(new QueryWrapper<Scenario>().eq("project_id", projectId));
Page<Scenario> page = new Page<>(pageNum, pageSize, true); Page<Scenario> page = new Page<>(pageNum, pageSize, true);
page.setRecords(records); return scenarioService.page(page, qw);
page.setTotal(total);
return page;
} }
private String currentUsername() { private String currentUsername() {

View File

@ -9,7 +9,7 @@ public interface CriticalDataService extends IService<CriticalData> {
/** /**
* 导入临界数据 * 导入临界数据
*/ */
boolean importCriticalData(MultipartFile file); boolean importCriticalData(MultipartFile file, String deviceType);
} }

View File

@ -7,5 +7,5 @@ public interface DeviceService extends IService<Device> {
/** /**
* 导入设备 * 导入设备
*/ */
boolean importDevices(MultipartFile file); boolean importDevices(MultipartFile file, String deviceType);
} }

View File

@ -40,7 +40,7 @@ public class CriticalDataServiceImpl
@Resource @Resource
private IUserService userService; private IUserService userService;
@Override @Override
public boolean importCriticalData(MultipartFile file) { public boolean importCriticalData(MultipartFile file, String deviceType) {
try { try {
String name = file.getOriginalFilename(); String name = file.getOriginalFilename();
log.info("critical-data import start name={} size={} contentType={}", name, file.getSize(), file.getContentType()); log.info("critical-data import start name={} size={} contentType={}", name, file.getSize(), file.getContentType());
@ -48,10 +48,10 @@ public class CriticalDataServiceImpl
String lower = name.toLowerCase(); String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) { if (lower.endsWith(".xlsx")) {
log.info("critical-data detected type=xlsx"); log.info("critical-data detected type=xlsx");
return importExcel(new XSSFWorkbook(file.getInputStream())); return importExcel(new XSSFWorkbook(file.getInputStream()), deviceType);
} else if (lower.endsWith(".xls")) { } else if (lower.endsWith(".xls")) {
log.info("critical-data detected type=xls"); log.info("critical-data detected type=xls");
return importExcel(new HSSFWorkbook(file.getInputStream())); return importExcel(new HSSFWorkbook(file.getInputStream()), deviceType);
} else { } else {
log.warn("critical-data unsupported file type name={}", name); log.warn("critical-data unsupported file type name={}", name);
return false; return false;
@ -62,7 +62,7 @@ public class CriticalDataServiceImpl
} }
} }
private boolean importExcel(Workbook workbook) { private boolean importExcel(Workbook workbook, String deviceType) {
try (Workbook wb = workbook) { try (Workbook wb = workbook) {
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll(); evaluator.evaluateAll();
@ -86,7 +86,7 @@ public class CriticalDataServiceImpl
} }
log.info("critical-data excel header keys={}", idx.keySet()); log.info("critical-data excel header keys={}", idx.keySet());
String[] keys = new String[]{ String[] keys = new String[]{
"device_type","diameter","height", "diameter","height",
"fissile_concentration","isotopic_abundance", "fissile_concentration","isotopic_abundance",
"keff_value","extra_features" "keff_value","extra_features"
}; };
@ -101,7 +101,7 @@ public class CriticalDataServiceImpl
Row row = sheet.getRow(r); Row row = sheet.getRow(r);
if (row == null) continue; if (row == null) continue;
CriticalData cd = new CriticalData(); CriticalData cd = new CriticalData();
cd.setDeviceType(cleanString(getString(row, idx.get("device_type")))); cd.setDeviceType(deviceType);
cd.setDiameter(cleanDecimal(getDecimalFlexible(row, idx.get("diameter"), evaluator, formatter))); cd.setDiameter(cleanDecimal(getDecimalFlexible(row, idx.get("diameter"), evaluator, formatter)));
cd.setHeight(cleanDecimal(getDecimalFlexible(row, idx.get("height"), evaluator, formatter))); cd.setHeight(cleanDecimal(getDecimalFlexible(row, idx.get("height"), evaluator, formatter)));
cd.setFissileConcentration(cleanDecimal(getDecimalFlexible(row, idx.get("fissile_concentration"), evaluator, formatter))); cd.setFissileConcentration(cleanDecimal(getDecimalFlexible(row, idx.get("fissile_concentration"), evaluator, formatter)));

View File

@ -35,15 +35,15 @@ public class DeviceServiceImpl
@Resource @Resource
private IUserService userService; private IUserService userService;
@Override @Override
public boolean importDevices(MultipartFile file) { public boolean importDevices(MultipartFile file, String deviceType) {
try { try {
String name = file.getOriginalFilename(); String name = file.getOriginalFilename();
if (name == null) return false; if (name == null) return false;
String lower = name.toLowerCase(); String lower = name.toLowerCase();
if (lower.endsWith(".xlsx")) { if (lower.endsWith(".xlsx")) {
return importExcel(new XSSFWorkbook(file.getInputStream())); return importExcel(new XSSFWorkbook(file.getInputStream()), deviceType);
} else if (lower.endsWith(".xls")) { } else if (lower.endsWith(".xls")) {
return importExcel(new HSSFWorkbook(file.getInputStream())); return importExcel(new HSSFWorkbook(file.getInputStream()), deviceType);
} else { } else {
return false; return false;
} }
@ -52,7 +52,7 @@ public class DeviceServiceImpl
} }
} }
private boolean importExcel(Workbook workbook) { private boolean importExcel(Workbook workbook, String deviceType) {
try (Workbook wb = workbook) { try (Workbook wb = workbook) {
Sheet sheet = wb.getSheetAt(0); Sheet sheet = wb.getSheetAt(0);
if (sheet == null) return false; if (sheet == null) return false;
@ -65,7 +65,7 @@ public class DeviceServiceImpl
String key = c.getStringCellValue(); String key = c.getStringCellValue();
if (key != null) idx.put(key.trim().toLowerCase(), i); if (key != null) idx.put(key.trim().toLowerCase(), i);
} }
String[] keys = new String[]{"type","code","name","size","volume","flow_rate","pulse_velocity"}; String[] keys = new String[]{"code","name","size","volume","flow_rate","pulse_velocity"};
for (String k : keys) { for (String k : keys) {
if (!idx.containsKey(k)) return false; if (!idx.containsKey(k)) return false;
} }
@ -74,7 +74,7 @@ public class DeviceServiceImpl
Row row = sheet.getRow(r); Row row = sheet.getRow(r);
if (row == null) continue; if (row == null) continue;
Device d = new Device(); Device d = new Device();
d.setType(getString(row, idx.get("type"))); d.setType(deviceType);
d.setCode(getString(row, idx.get("code"))); d.setCode(getString(row, idx.get("code")));
d.setName(getString(row, idx.get("name"))); d.setName(getString(row, idx.get("name")));
d.setSize(validateJson(getString(row, idx.get("size")))); d.setSize(validateJson(getString(row, idx.get("size"))));

View File

@ -196,6 +196,18 @@ public class ProjectServiceImpl
plan.put("target", Map.of("entityType","device","entityId",deviceId,"property",propName)); plan.put("target", Map.of("entityType","device","entityId",deviceId,"property",propName));
plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0); plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0);
plan.put("sources", srcList); plan.put("sources", srcList);
StringBuilder sb = new StringBuilder();
double b = prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0;
sb.append(b);
for (Map<String, Object> s : srcList) {
double coef = s.get("coefficient") instanceof Number ? ((Number) s.get("coefficient")).doubleValue() : 1.0;
String seId = String.valueOf(s.get("entityId"));
String seProp = String.valueOf(s.get("property"));
long dms = s.get("delayMs") instanceof Number ? ((Number) s.get("delayMs")).longValue() : 0L;
long ds = dms / 1000L;
sb.append(" + ").append(coef).append("*").append(seId).append(".").append(seProp).append("@").append(ds).append("s");
}
plan.put("formula", sb.toString());
plans.add(plan); plans.add(plan);
} }
}); });
@ -304,6 +316,18 @@ public class ProjectServiceImpl
plan.put("target", Map.of("entityType","material","entityId",mid,"property",propName)); plan.put("target", Map.of("entityType","material","entityId",mid,"property",propName));
plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0); plan.put("bias", prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0);
plan.put("sources", srcList); plan.put("sources", srcList);
StringBuilder sb = new StringBuilder();
double b = prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0;
sb.append(b);
for (Map<String, Object> s : srcList) {
double coef = s.get("coefficient") instanceof Number ? ((Number) s.get("coefficient")).doubleValue() : 1.0;
String seId = String.valueOf(s.get("entityId"));
String seProp = String.valueOf(s.get("property"));
long dms = s.get("delayMs") instanceof Number ? ((Number) s.get("delayMs")).longValue() : 0L;
long ds = dms / 1000L;
sb.append(" + ").append(coef).append("*").append(seId).append(".").append(seProp).append("@").append(ds).append("s");
}
plan.put("formula", sb.toString());
plans.add(plan); plans.add(plan);
} }
}); });
@ -497,10 +521,6 @@ public class ProjectServiceImpl
return out; return out;
} }
JsonNode root = objectMapper.readTree(p.getTopology()); JsonNode root = objectMapper.readTree(p.getTopology());
long start = getLongParam(params, "startTime", 0L);
long end = getLongParam(params, "endTime", 60L);
long stepSec = getLongParam(params, "step", 1L);
if (end < start) end = start;
List<Device> devs = parseDeviceOrder(projectId); List<Device> devs = parseDeviceOrder(projectId);
if (devs.isEmpty()) { if (devs.isEmpty()) {
issues.add("devices为空"); issues.add("devices为空");
@ -508,27 +528,42 @@ public class ProjectServiceImpl
out.put("frames", frames); out.put("frames", frames);
return out; return out;
} }
Map<String, String> devToMat = buildDeviceMaterialMap(root, issues);
Map<String, Map<String, Double>> devStatic = new HashMap<>(); Map<String, Map<String, Double>> devStatic = new HashMap<>();
Map<String, Map<String, Object>> devInfluence = new HashMap<>(); Map<String, Map<String, Object>> devInfluence = new HashMap<>();
parseDeviceStaticsAndInfluences(root, devStatic, devInfluence, issues); parseDeviceStaticsAndInfluences(root, devStatic, devInfluence, issues);
Map<String, Map<String, Double>> matStatic = new HashMap<>(); Map<String, Map<String, Double>> matStatic = new HashMap<>();
Map<String, Map<String, Object>> matInfluence = new HashMap<>(); Map<String, Map<String, Object>> matInfluence = new HashMap<>();
parseMaterialStaticsAndInfluences(root, matStatic, matInfluence, issues); parseMaterialStaticsAndInfluences(root, matStatic, matInfluence, issues);
Map<String, Map<String, Double>> matStaticDb = buildMaterialStaticFromDb(devToMat);
List<Event> events = eventService.list( List<Event> events = eventService.list(
new QueryWrapper<Event>() new QueryWrapper<Event>()
.select("event_id","scenario_id","device_id","material_id","attr_changes","trigger_time","created_at","modifier") .select("event_id","scenario_id","device_id","material_id","attr_changes","trigger_time","created_at","modifier")
.eq("scenario_id", scenarioId) .eq("scenario_id", scenarioId)
); );
Map<String, Object> valueProviders = buildValueProviders(events, issues); Map<String, Object> valueProviders = buildValueProviders(events, issues);
List<Long> timePoints = collectTimePoints(valueProviders);
if (timePoints.isEmpty()) {
issues.add("事件为空或未提供时间点,无法生成帧");
out.put("generated", Map.of("events", events.size(), "snapshots", 0));
out.put("frames", frames);
return out;
}
Map<String, String> devTypeMap = buildDeviceTypeMap(root);
int snapshots = 0; int snapshots = 0;
for (long t = start; t <= end; t += stepSec) { for (int idx = 0; idx < timePoints.size(); idx++) {
int stepIndex = (int) ((t - start) / stepSec); long t = timePoints.get(idx);
int stepIndex = idx;
Map<String, Map<String, Object>> deviceStates = new HashMap<>(); Map<String, Map<String, Object>> deviceStates = new HashMap<>();
for (Device d : devs) { for (Device d : devs) {
String did = d.getDeviceId(); String did = d.getDeviceId();
String dtype = devTypeMap.getOrDefault(did, d.getType());
Map<String, Object> state = new HashMap<>(); Map<String, Object> state = new HashMap<>();
Map<String, Double> s = devStatic.getOrDefault(did, Map.of()); if (dtype != null && !dtype.isEmpty()) {
for (Map.Entry<String, Double> e : s.entrySet()) state.put(e.getKey(), e.getValue()); state.put("deviceType", dtype);
}
Map<String, Double> sdev = devStatic.getOrDefault(did, Map.of());
for (Map.Entry<String, Double> e : sdev.entrySet()) state.put(e.getKey(), e.getValue());
Map<String, Object> infl = devInfluence.getOrDefault(did, Map.of()); Map<String, Object> infl = devInfluence.getOrDefault(did, Map.of());
for (Map.Entry<String, Object> e : infl.entrySet()) { for (Map.Entry<String, Object> e : infl.entrySet()) {
String prop = e.getKey(); String prop = e.getKey();
@ -552,12 +587,17 @@ public class ProjectServiceImpl
} }
state.put(prop, sum + bias); state.put(prop, sum + bias);
} }
Map<String, Object> materialsState = new HashMap<>(); String boundMid = devToMat.get(did);
for (Map.Entry<String, Map<String, Double>> me : matStatic.entrySet()) { if (boundMid != null) {
String mid = me.getKey(); Map<String, Double> smatDb = matStaticDb.getOrDefault(boundMid, Map.of());
Map<String, Object> mstate = new HashMap<>(); for (Map.Entry<String, Double> e : smatDb.entrySet()) {
for (Map.Entry<String, Double> e : me.getValue().entrySet()) mstate.put(e.getKey(), e.getValue()); state.put(e.getKey(), e.getValue());
Map<String, Object> minfl = matInfluence.getOrDefault(mid, Map.of()); }
Map<String, Double> smatTopo = matStatic.getOrDefault(boundMid, Map.of());
for (Map.Entry<String, Double> e : smatTopo.entrySet()) {
state.put(e.getKey(), e.getValue());
}
Map<String, Object> minfl = matInfluence.getOrDefault(boundMid, Map.of());
for (Map.Entry<String, Object> e : minfl.entrySet()) { for (Map.Entry<String, Object> e : minfl.entrySet()) {
String prop = e.getKey(); String prop = e.getKey();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -578,11 +618,13 @@ public class ProjectServiceImpl
sum += coef * val; sum += coef * val;
} }
} }
mstate.put(prop, sum + bias); state.put(prop, sum + bias);
} }
materialsState.put(mid, mstate); // apply material event overrides
overrideWithEvents(state, "material", boundMid, t, valueProviders);
} }
state.put("materials", materialsState); // apply device event overrides
overrideWithEvents(state, "device", did, t, valueProviders);
deviceStates.put(did, state); deviceStates.put(did, state);
} }
snapshots += devs.size(); snapshots += devs.size();
@ -834,6 +876,112 @@ public class ProjectServiceImpl
return s.getOrDefault(property, 0.0); return s.getOrDefault(property, 0.0);
} }
private List<Long> collectTimePoints(Map<String, Object> providers) {
Set<Long> ts = new HashSet<>();
for (Object v : providers.values()) {
@SuppressWarnings("unchecked")
Map<String, Object> pv = (Map<String, Object>) v;
@SuppressWarnings("unchecked")
List<Map<String, Object>> schedule = (List<Map<String, Object>>) pv.get("schedule");
if (schedule == null) continue;
for (Map<String, Object> s : schedule) {
String type = String.valueOf(s.get("type"));
if ("step-set".equals(type)) {
long t = (long) toDouble(s.get("time"));
ts.add(t);
} else if ("ramp".equals(type)) {
long st = (long) toDouble(s.get("startTime"));
long et = (long) toDouble(s.get("endTime"));
ts.add(st);
ts.add(et);
}
}
}
List<Long> list = new ArrayList<>(ts);
list.sort(Comparator.naturalOrder());
return list;
}
private Map<String, String> buildDeviceMaterialMap(JsonNode root, List<String> issues) {
Map<String, String> map = new HashMap<>();
JsonNode devicesNode = root.path("devices");
if (!devicesNode.isArray()) return map;
for (JsonNode dn : devicesNode) {
String did = optText(dn, "deviceId");
if (did == null || did.isEmpty()) continue;
JsonNode mats = dn.path("materials");
if (mats.isMissingNode() || mats.isNull()) mats = dn.path("material");
if (mats.isArray()) {
for (JsonNode mn : mats) {
String mid = optText(mn, "materialId");
if (mid != null && !mid.isEmpty()) {
map.put(did, mid);
break;
}
}
} else if (mats.isObject()) {
String mid = optText(mats, "materialId");
if (mid != null && !mid.isEmpty()) {
map.put(did, mid);
} else {
issues.add("材料缺少materialId: device=" + did);
}
}
}
return map;
}
private Map<String, String> buildDeviceTypeMap(JsonNode root) {
Map<String, String> map = new HashMap<>();
JsonNode devicesNode = root.path("devices");
if (!devicesNode.isArray()) return map;
for (JsonNode dn : devicesNode) {
String did = optText(dn, "deviceId");
String tp = optText(dn, "type");
if (did != null && !did.isEmpty() && tp != null && !tp.isEmpty()) {
map.put(did, tp);
}
}
return map;
}
private void overrideWithEvents(Map<String, Object> state, String entityType, String entityId, long t, Map<String, Object> providers) {
String prefix = entityType + ":" + entityId + ":";
for (String key : providers.keySet()) {
if (!key.startsWith(prefix)) continue;
String prop = key.substring(prefix.length());
double val = readValue(entityType, entityId, prop, t, Map.of(), Map.of(), providers);
state.put(prop, val);
}
}
private Map<String, Map<String, Double>> buildMaterialStaticFromDb(Map<String, String> devToMat) {
Map<String, Map<String, Double>> out = new HashMap<>();
if (devToMat == null || devToMat.isEmpty()) return out;
Set<String> mids = new HashSet<>(devToMat.values());
if (mids.isEmpty()) return out;
List<Material> mats = materialService.list(new QueryWrapper<Material>().in("material_id", mids));
Map<String, Material> mm = new HashMap<>();
for (Material m : mats) mm.put(m.getMaterialId(), m);
for (String mid : mids) {
Material m = mm.get(mid);
if (m == null) continue;
Map<String, Double> s = new HashMap<>();
if (m.getUConcentration() != null) s.put("u_concentration", toDouble(m.getUConcentration()));
if (m.getUo2Density() != null) s.put("uo2_density", toDouble(m.getUo2Density()));
if (m.getUEnrichment() != null) s.put("u_enrichment", toDouble(m.getUEnrichment()));
if (m.getPuConcentration() != null) s.put("pu_concentration", toDouble(m.getPuConcentration()));
if (m.getPuo2Density() != null) s.put("puo2_density", toDouble(m.getPuo2Density()));
if (m.getPuIsotope() != null) s.put("pu_isotope", toDouble(m.getPuIsotope()));
if (m.getHno3Acidity() != null) s.put("hno3_acidity", toDouble(m.getHno3Acidity()));
if (m.getH2c2o4Concentration() != null) s.put("h2c2o4_concentration", toDouble(m.getH2c2o4Concentration()));
if (m.getOrganicRatio() != null) s.put("organic_ratio", toDouble(m.getOrganicRatio()));
if (m.getMoistureContent() != null) s.put("moisture_content", toDouble(m.getMoistureContent()));
out.put(mid, s);
}
return out;
}
private long getLongParam(Map<String, Object> params, String key, long def) { private long getLongParam(Map<String, Object> params, String key, long def) {
if (params == null) return def; if (params == null) return def;
Object v = params.get(key); Object v = params.get(key);