diff --git a/business-css/src/main/java/com/yfd/business/css/config/MybatisConfig.java b/business-css/src/main/java/com/yfd/business/css/config/MybatisConfig.java index 8772a42..76f7165 100644 --- a/business-css/src/main/java/com/yfd/business/css/config/MybatisConfig.java +++ b/business-css/src/main/java/com/yfd/business/css/config/MybatisConfig.java @@ -1,23 +1,30 @@ package com.yfd.business.css.config; import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.plugin.Interceptor; import org.mybatis.spring.SqlSessionTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import jakarta.annotation.Resource; import javax.sql.DataSource; @Configuration public class MybatisConfig { + @Resource + private MybatisPlusInterceptor mybatisPlusInterceptor; + @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath*:/mapper/**/*.xml")); + factoryBean.setPlugins(new Interceptor[]{ mybatisPlusInterceptor }); return factoryBean.getObject(); } diff --git a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java index 2314d58..d716431 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/AlgorithmController.java @@ -66,20 +66,13 @@ public class AlgorithmController { public Page searchAlgorithms(@RequestParam(required = false) String name, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); QueryWrapper qw = new QueryWrapper<>(); if (name != null && !name.isEmpty()) { qw.like("name", name); } - qw.orderByDesc("updated_at").last("LIMIT " + offset + "," + pageSize); - java.util.List records = algorithmService.list(qw); - long total = (name != null && !name.isEmpty()) - ? algorithmService.count(new QueryWrapper().like("name", name)) - : algorithmService.count(); + qw.orderByDesc("updated_at"); Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + return algorithmService.page(page, qw); } private String currentUsername() { diff --git a/business-css/src/main/java/com/yfd/business/css/controller/CriticalDataController.java b/business-css/src/main/java/com/yfd/business/css/controller/CriticalDataController.java index 245debf..9f023b1 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/CriticalDataController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/CriticalDataController.java @@ -76,8 +76,9 @@ public class CriticalDataController { * @return 是否导入成功 */ @PostMapping("/import") - public boolean importCriticalData(@RequestParam("file") MultipartFile file) { - return criticalDataService.importCriticalData(file); + public boolean importCriticalData(@RequestParam("file") MultipartFile file, + @RequestParam String deviceType) { + return criticalDataService.importCriticalData(file, deviceType); } @@ -95,16 +96,10 @@ public class CriticalDataController { public Page listByDeviceType(@RequestParam String deviceType, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); + Page page = new Page<>(pageNum, pageSize, true); QueryWrapper qw = new QueryWrapper() .eq("device_type", deviceType) - .orderByDesc("created_at") - .last("LIMIT " + offset + "," + pageSize); - java.util.List records = criticalDataService.list(qw); - long total = criticalDataService.count(new QueryWrapper().eq("device_type", deviceType)); - Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + .orderByDesc("created_at"); + return criticalDataService.page(page, qw); } } diff --git a/business-css/src/main/java/com/yfd/business/css/controller/DeviceController.java b/business-css/src/main/java/com/yfd/business/css/controller/DeviceController.java index ccc0cdd..64f0ecb 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/DeviceController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/DeviceController.java @@ -85,8 +85,9 @@ public class DeviceController { * @return 是否导入成功 */ @PostMapping("/import") - public boolean importDevices(@RequestParam("file") MultipartFile file) { - return deviceService.importDevices(file); + public boolean importDevices(@RequestParam("file") MultipartFile file, + @RequestParam String deviceType) { + return deviceService.importDevices(file, deviceType); } @@ -124,7 +125,6 @@ public class DeviceController { @RequestParam(required = false) String name, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); QueryWrapper qw = new QueryWrapper<>(); if (type != null && !type.isEmpty()) { qw.eq("type", type); @@ -132,20 +132,9 @@ public class DeviceController { if (name != null && !name.isEmpty()) { qw.like("name", name); } - qw.orderByDesc("created_at").last("LIMIT " + offset + "," + pageSize); - java.util.List records = deviceService.list(qw); - QueryWrapper 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); + qw.orderByDesc("created_at"); Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + return deviceService.page(page, qw); } private String currentUsername() { diff --git a/business-css/src/main/java/com/yfd/business/css/controller/MaterialController.java b/business-css/src/main/java/com/yfd/business/css/controller/MaterialController.java index 6785128..7cedfb7 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/MaterialController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/MaterialController.java @@ -102,37 +102,23 @@ public class MaterialController { public Page search(@RequestParam(required = false) String name, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); QueryWrapper qw = new QueryWrapper().orderByDesc("created_at"); if (name != null && !name.isEmpty()) { qw.like("name", name); } - qw.last("LIMIT " + offset + "," + pageSize); - java.util.List records = materialService.list(qw); - long total = (name != null && !name.isEmpty()) - ? materialService.count(new QueryWrapper().like("name", name)) - : materialService.count(); Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + return materialService.page(page, qw); } @GetMapping("/by-project") public Page pageByProject(@RequestParam String projectId, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); QueryWrapper qw = new QueryWrapper() .eq("project_id", projectId) - .orderByDesc("created_at") - .last("LIMIT " + offset + "," + pageSize); - java.util.List records = materialService.list(qw); - long total = materialService.count(new QueryWrapper().eq("project_id", projectId)); + .orderByDesc("created_at"); Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + return materialService.page(page, qw); } private String currentUsername() { diff --git a/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java b/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java index 575565c..47168d3 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/ProjectController.java @@ -156,23 +156,12 @@ public class ProjectController { public Page search(@RequestParam(required = false) @Parameter(description = "项目名称关键词,可为空") String name, @RequestParam(defaultValue = "1") @Parameter(description = "页码,默认1") long pageNum, @RequestParam(defaultValue = "20") @Parameter(description = "每页条数,默认20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); QueryWrapper qw = new QueryWrapper().orderByDesc("created_at"); if (name != null && !name.isEmpty()) { qw.like("name", name); } - qw.last("LIMIT " + offset + "," + pageSize); - java.util.List records = projectService.list(qw); - long total; - if (name != null && !name.isEmpty()) { - total = projectService.count(new QueryWrapper().like("name", name)); - } else { - total = projectService.count(); - } Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + return projectService.page(page, qw); } /** diff --git a/business-css/src/main/java/com/yfd/business/css/controller/ScenarioController.java b/business-css/src/main/java/com/yfd/business/css/controller/ScenarioController.java index c7a286c..95562f2 100644 --- a/business-css/src/main/java/com/yfd/business/css/controller/ScenarioController.java +++ b/business-css/src/main/java/com/yfd/business/css/controller/ScenarioController.java @@ -103,20 +103,12 @@ public class ScenarioController { @RequestParam(required = false) String name, @RequestParam(defaultValue = "1") long pageNum, @RequestParam(defaultValue = "20") long pageSize) { - long offset = Math.max(0, (pageNum - 1) * pageSize); QueryWrapper qw = new QueryWrapper().eq("project_id", projectId).orderByDesc("created_at"); if (name != null && !name.isEmpty()) { qw.like("name", name); } - qw.last("LIMIT " + offset + "," + pageSize); - java.util.List records = scenarioService.list(qw); - long total = (name != null && !name.isEmpty()) - ? scenarioService.count(new QueryWrapper().eq("project_id", projectId).like("name", name)) - : scenarioService.count(new QueryWrapper().eq("project_id", projectId)); Page page = new Page<>(pageNum, pageSize, true); - page.setRecords(records); - page.setTotal(total); - return page; + return scenarioService.page(page, qw); } private String currentUsername() { diff --git a/business-css/src/main/java/com/yfd/business/css/service/CriticalDataService.java b/business-css/src/main/java/com/yfd/business/css/service/CriticalDataService.java index e4ac9b1..6bfcf34 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/CriticalDataService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/CriticalDataService.java @@ -9,7 +9,7 @@ public interface CriticalDataService extends IService { /** * 导入临界数据 */ - boolean importCriticalData(MultipartFile file); + boolean importCriticalData(MultipartFile file, String deviceType); } diff --git a/business-css/src/main/java/com/yfd/business/css/service/DeviceService.java b/business-css/src/main/java/com/yfd/business/css/service/DeviceService.java index 8d20810..1231bc1 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/DeviceService.java +++ b/business-css/src/main/java/com/yfd/business/css/service/DeviceService.java @@ -7,5 +7,5 @@ public interface DeviceService extends IService { /** * 导入设备 */ - boolean importDevices(MultipartFile file); + boolean importDevices(MultipartFile file, String deviceType); } diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/CriticalDataServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/CriticalDataServiceImpl.java index 488547d..8ba9927 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/CriticalDataServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/CriticalDataServiceImpl.java @@ -40,7 +40,7 @@ public class CriticalDataServiceImpl @Resource private IUserService userService; @Override - public boolean importCriticalData(MultipartFile file) { + public boolean importCriticalData(MultipartFile file, String deviceType) { try { String name = file.getOriginalFilename(); 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(); if (lower.endsWith(".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")) { log.info("critical-data detected type=xls"); - return importExcel(new HSSFWorkbook(file.getInputStream())); + return importExcel(new HSSFWorkbook(file.getInputStream()), deviceType); } else { log.warn("critical-data unsupported file type name={}", name); 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) { FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); evaluator.evaluateAll(); @@ -86,7 +86,7 @@ public class CriticalDataServiceImpl } log.info("critical-data excel header keys={}", idx.keySet()); String[] keys = new String[]{ - "device_type","diameter","height", + "diameter","height", "fissile_concentration","isotopic_abundance", "keff_value","extra_features" }; @@ -101,7 +101,7 @@ public class CriticalDataServiceImpl Row row = sheet.getRow(r); if (row == null) continue; 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.setHeight(cleanDecimal(getDecimalFlexible(row, idx.get("height"), evaluator, formatter))); cd.setFissileConcentration(cleanDecimal(getDecimalFlexible(row, idx.get("fissile_concentration"), evaluator, formatter))); diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java index be2e44b..34e5f5c 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/DeviceServiceImpl.java @@ -35,15 +35,15 @@ public class DeviceServiceImpl @Resource private IUserService userService; @Override - public boolean importDevices(MultipartFile file) { + public boolean importDevices(MultipartFile file, String deviceType) { try { String name = file.getOriginalFilename(); if (name == null) return false; String lower = name.toLowerCase(); if (lower.endsWith(".xlsx")) { - return importExcel(new XSSFWorkbook(file.getInputStream())); + return importExcel(new XSSFWorkbook(file.getInputStream()), deviceType); } else if (lower.endsWith(".xls")) { - return importExcel(new HSSFWorkbook(file.getInputStream())); + return importExcel(new HSSFWorkbook(file.getInputStream()), deviceType); } else { 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) { Sheet sheet = wb.getSheetAt(0); if (sheet == null) return false; @@ -65,7 +65,7 @@ public class DeviceServiceImpl String key = c.getStringCellValue(); 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) { if (!idx.containsKey(k)) return false; } @@ -74,7 +74,7 @@ public class DeviceServiceImpl Row row = sheet.getRow(r); if (row == null) continue; Device d = new Device(); - d.setType(getString(row, idx.get("type"))); + d.setType(deviceType); d.setCode(getString(row, idx.get("code"))); d.setName(getString(row, idx.get("name"))); d.setSize(validateJson(getString(row, idx.get("size")))); diff --git a/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java b/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java index cfc4f8c..b0a3b1d 100644 --- a/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java +++ b/business-css/src/main/java/com/yfd/business/css/service/impl/ProjectServiceImpl.java @@ -190,17 +190,29 @@ public class ProjectServiceImpl src.put("delayMs", delayMs); srcList.add(src); } - } } - Map plan = new HashMap<>(); - 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("sources", srcList); - plans.add(plan); } - }); - } - // materials can be array or single object; also some payloads use "material" key + Map plan = new HashMap<>(); + 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("sources", srcList); + StringBuilder sb = new StringBuilder(); + double b = prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0; + sb.append(b); + for (Map 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); + } + }); + } + // materials can be array or single object; also some payloads use "material" key JsonNode mats = dn.path("materials"); if (mats.isMissingNode() || mats.isNull()) { mats = dn.path("material"); @@ -298,16 +310,28 @@ public class ProjectServiceImpl src.put("delayMs", delayMs); srcList.add(src); } - } } - Map plan = new HashMap<>(); - 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("sources", srcList); - plans.add(plan); } - }); - } + Map plan = new HashMap<>(); + 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("sources", srcList); + StringBuilder sb = new StringBuilder(); + double b = prop.path("bias").isNumber() ? prop.path("bias").asDouble() : 0.0; + sb.append(b); + for (Map 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); + } + }); + } } private static String optText(JsonNode n, String field) { @@ -497,10 +521,6 @@ public class ProjectServiceImpl return out; } 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 devs = parseDeviceOrder(projectId); if (devs.isEmpty()) { issues.add("devices为空"); @@ -508,27 +528,42 @@ public class ProjectServiceImpl out.put("frames", frames); return out; } + Map devToMat = buildDeviceMaterialMap(root, issues); Map> devStatic = new HashMap<>(); Map> devInfluence = new HashMap<>(); parseDeviceStaticsAndInfluences(root, devStatic, devInfluence, issues); Map> matStatic = new HashMap<>(); Map> matInfluence = new HashMap<>(); parseMaterialStaticsAndInfluences(root, matStatic, matInfluence, issues); + Map> matStaticDb = buildMaterialStaticFromDb(devToMat); List events = eventService.list( new QueryWrapper() .select("event_id","scenario_id","device_id","material_id","attr_changes","trigger_time","created_at","modifier") .eq("scenario_id", scenarioId) ); Map valueProviders = buildValueProviders(events, issues); + List 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 devTypeMap = buildDeviceTypeMap(root); int snapshots = 0; - for (long t = start; t <= end; t += stepSec) { - int stepIndex = (int) ((t - start) / stepSec); + for (int idx = 0; idx < timePoints.size(); idx++) { + long t = timePoints.get(idx); + int stepIndex = idx; Map> deviceStates = new HashMap<>(); for (Device d : devs) { String did = d.getDeviceId(); + String dtype = devTypeMap.getOrDefault(did, d.getType()); Map state = new HashMap<>(); - Map s = devStatic.getOrDefault(did, Map.of()); - for (Map.Entry e : s.entrySet()) state.put(e.getKey(), e.getValue()); + if (dtype != null && !dtype.isEmpty()) { + state.put("deviceType", dtype); + } + Map sdev = devStatic.getOrDefault(did, Map.of()); + for (Map.Entry e : sdev.entrySet()) state.put(e.getKey(), e.getValue()); Map infl = devInfluence.getOrDefault(did, Map.of()); for (Map.Entry e : infl.entrySet()) { String prop = e.getKey(); @@ -552,12 +587,17 @@ public class ProjectServiceImpl } state.put(prop, sum + bias); } - Map materialsState = new HashMap<>(); - for (Map.Entry> me : matStatic.entrySet()) { - String mid = me.getKey(); - Map mstate = new HashMap<>(); - for (Map.Entry e : me.getValue().entrySet()) mstate.put(e.getKey(), e.getValue()); - Map minfl = matInfluence.getOrDefault(mid, Map.of()); + String boundMid = devToMat.get(did); + if (boundMid != null) { + Map smatDb = matStaticDb.getOrDefault(boundMid, Map.of()); + for (Map.Entry e : smatDb.entrySet()) { + state.put(e.getKey(), e.getValue()); + } + Map smatTopo = matStatic.getOrDefault(boundMid, Map.of()); + for (Map.Entry e : smatTopo.entrySet()) { + state.put(e.getKey(), e.getValue()); + } + Map minfl = matInfluence.getOrDefault(boundMid, Map.of()); for (Map.Entry e : minfl.entrySet()) { String prop = e.getKey(); @SuppressWarnings("unchecked") @@ -578,11 +618,13 @@ public class ProjectServiceImpl sum += coef * val; } } - mstate.put(prop, sum + bias); + state.put(prop, sum + bias); } - materialsState.put(mid, mstate); - } - state.put("materials", materialsState); + // apply material event overrides + overrideWithEvents(state, "material", boundMid, t, valueProviders); + } + // apply device event overrides + overrideWithEvents(state, "device", did, t, valueProviders); deviceStates.put(did, state); } snapshots += devs.size(); @@ -834,6 +876,112 @@ public class ProjectServiceImpl return s.getOrDefault(property, 0.0); } + private List collectTimePoints(Map providers) { + Set ts = new HashSet<>(); + for (Object v : providers.values()) { + @SuppressWarnings("unchecked") + Map pv = (Map) v; + @SuppressWarnings("unchecked") + List> schedule = (List>) pv.get("schedule"); + if (schedule == null) continue; + for (Map 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 list = new ArrayList<>(ts); + list.sort(Comparator.naturalOrder()); + return list; + } + + private Map buildDeviceMaterialMap(JsonNode root, List issues) { + Map 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 buildDeviceTypeMap(JsonNode root) { + Map 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 state, String entityType, String entityId, long t, Map 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> buildMaterialStaticFromDb(Map devToMat) { + Map> out = new HashMap<>(); + if (devToMat == null || devToMat.isEmpty()) return out; + Set mids = new HashSet<>(devToMat.values()); + if (mids.isEmpty()) return out; + List mats = materialService.list(new QueryWrapper().in("material_id", mids)); + Map 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 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 params, String key, long def) { if (params == null) return def; Object v = params.get(key);