| | |
| | | import java.time.Instant; |
| | | import java.time.OffsetDateTime; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneOffset; |
| | | import java.time.ZoneId; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | import java.util.HashMap; |
| | |
| | | this.tests = tests; |
| | | } |
| | | |
| | | /** 查询测试当前有效的检测柜与采集器实时观测,并补充可筛选的工步上下文。 */ |
| | | @Override public List<Map<String, Object>> records(Long testId) |
| | | { |
| | | requireTest(testId); |
| | |
| | | List<Map<String, Object>> collector = mapper.selectPersistedCollectorRecords(testId); |
| | | if (cabinet != null) source.addAll(cabinet); |
| | | if (collector != null) source.addAll(collector); |
| | | attachCollectorStepContext(source); |
| | | return applyEffectiveData(testId, "REALTIME", source); |
| | | List<Map<String, Object>> effective = applyEffectiveData(testId, "REALTIME", source); |
| | | attachCollectorStepContext(effective); |
| | | return effective; |
| | | } |
| | | |
| | | @Override public List<Map<String, Object>> steps(Long testId) |
| | |
| | | /** 按检测柜实际工步的设备时间边界给采集器样本补充筛选上下文,不配对采样点。 */ |
| | | private void attachCollectorStepContext(List<Map<String, Object>> rows) |
| | | { |
| | | List<StepWindow> windows = new ArrayList<>(); |
| | | Map<String, StepWindowBuilder> builders = new LinkedHashMap<>(); |
| | | for (Map<String, Object> row : rows) |
| | | { |
| | |
| | | builder.start = builder.start.isAfter(time) ? time : builder.start; |
| | | builder.end = builder.end == null || builder.end.isBefore(time) ? time : builder.end; |
| | | } |
| | | for (StepWindowBuilder builder : builders.values()) windows.add(builder.build()); |
| | | windows.sort(Comparator.comparing(StepWindow::start)); |
| | | for (int index = 0; index < windows.size(); index++) |
| | | Map<String, List<StepWindow>> windowsByBattery = new LinkedHashMap<>(); |
| | | for (StepWindowBuilder builder : builders.values()) |
| | | windowsByBattery.computeIfAbsent(builder.batteryNo, ignored -> new ArrayList<>()).add(builder.build()); |
| | | for (List<StepWindow> batteryWindows : windowsByBattery.values()) |
| | | { |
| | | StepWindow current = windows.get(index); |
| | | Instant nextStart = windows.stream() |
| | | .filter(window -> window.batteryNo().equals(current.batteryNo()) && window.start().isAfter(current.start())) |
| | | .map(StepWindow::start).min(Comparator.naturalOrder()).orElse(null); |
| | | windows.set(index, current.withNextStart(nextStart)); |
| | | batteryWindows.sort(Comparator.comparing(StepWindow::start)); |
| | | for (int index = 0; index < batteryWindows.size(); index++) |
| | | { |
| | | StepWindow current = batteryWindows.get(index); |
| | | Instant nextStart = index + 1 < batteryWindows.size() ? batteryWindows.get(index + 1).start() : null; |
| | | batteryWindows.set(index, current.withNextStart(nextStart)); |
| | | } |
| | | } |
| | | for (Map<String, Object> row : rows) |
| | | { |
| | | if (!"COLLECTOR".equals(String.valueOf(row.get("sourceType")))) continue; |
| | | Instant time = toInstant(row.get("deviceTime")); |
| | | if (time == null) continue; |
| | | StepWindow match = windows.stream() |
| | | .filter(window -> text(row, "batteryNo").equals(window.batteryNo())) |
| | | List<StepWindow> batteryWindows = windowsByBattery.getOrDefault(text(row, "batteryNo"), List.of()); |
| | | StepWindow match = batteryWindows.stream() |
| | | .filter(window -> !time.isBefore(window.start()) |
| | | && (window.nextStart() == null || time.isBefore(window.nextStart()))) |
| | | .findFirst().orElse(null); |
| | |
| | | catch (RuntimeException ignored) { } |
| | | try { return OffsetDateTime.parse(text).toInstant(); } |
| | | catch (RuntimeException ignored) { } |
| | | try { return LocalDateTime.parse(text).toInstant(ZoneOffset.UTC); } |
| | | try { return LocalDateTime.parse(text).atZone(ZoneId.of("Asia/Hong_Kong")).toInstant(); } |
| | | catch (RuntimeException ignored) { return null; } |
| | | } |
| | | |