| | |
| | | { this.tests = tests; this.data = data; this.reports = reports; this.definitions = definitions; } |
| | | |
| | | public record Candidate(Selection selection, String name, String type, long groupId, String batteryGroup, String batteryNo) { } |
| | | public record ChargeStep(int stepNo, String name, String stepType, BigDecimal endVoltage) { } |
| | | public record Item(Candidate source, List<Integer> cycleNos, List<Map<String, Object>> cycles, |
| | | Map<String, Map<String, BigDecimal>> metrics, List<CurrentBatteryField> businessFields) { } |
| | | Map<String, Map<String, BigDecimal>> metrics, List<CurrentBatteryField> businessFields, |
| | | Map<Integer, List<ChargeStep>> chargeSteps) { } |
| | | public record Matrix(List<Item> items, List<Integer> cycleNos) { } |
| | | public record Summary(long id, String name, String creator, long createdAt, String testName, String batteryGroup) { } |
| | | |
| | |
| | | var actual = Stream.of(rows, batteryRows(records.computeIfAbsent(selection.testId(), data::records), battery), |
| | | batteryRows(steps.computeIfAbsent(selection.testId(), data::steps), battery)).flatMap(List::stream) |
| | | .map(this::cycleNo).filter(no -> no > 0).distinct().sorted().toList(); |
| | | items.add(new Item(candidate(test, battery), actual, rows, metrics(rows, actual), battery.getFields())); |
| | | items.add(new Item(candidate(test, battery), actual, rows, metrics(rows, actual), battery.getFields(), chargeSteps(steps.get(selection.testId()), battery, actual))); |
| | | } |
| | | return new Matrix(items, items.stream().flatMap(item -> item.cycleNos().stream()).distinct().sorted().toList()); |
| | | } |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Map<String, Object> create(FormationReportRequest request, long userId, String userName) |
| | | { |
| | | Settings settings = new Settings(validateFields(request.settings().fields(), true), request.settings().values()); |
| | | Settings settings = new Settings(validateFields(request.settings().fields(), true), request.settings().values(), |
| | | request.settings().chargeStepSelections() == null ? Map.of() : request.settings().chargeStepSelections()); |
| | | Matrix matrix = freeze(preview(request.selections()), settings.fields()); |
| | | validateValues(settings, matrix.items()); |
| | | validateValues(settings, matrix.items()); validateChargeSelections(settings, matrix.items()); |
| | | var snapshot = new LinkedHashMap<String, Object>(); |
| | | snapshot.put("name", request.name().trim()); snapshot.put("description", request.description()); |
| | | snapshot.put("items", matrix.items()); snapshot.put("cycleNos", matrix.cycleNos()); snapshot.put("settings", settings); |
| | |
| | | { |
| | | var snapshot = current(id, request.baseVersionNo()); |
| | | validateFields(request.settings().fields(), false); |
| | | validateValues(request.settings(), items(snapshot)); |
| | | validateValues(request.settings(), items(snapshot)); validateChargeSelections(request.settings(), items(snapshot)); |
| | | snapshot.put("name", request.name().trim()); snapshot.put("description", request.description()); snapshot.put("settings", request.settings()); |
| | | reports.append(id, request.baseVersionNo() + 1, request.name().trim(), json(snapshot), userId, userName, request.changeSummary().trim()); |
| | | return get(id, null); |
| | |
| | | Set<String> allowed = valueKeys(settings.fields(), matrix.items()); |
| | | Map<String, String> values = new LinkedHashMap<>(); |
| | | settings.values().forEach((key, value) -> { if (allowed.contains(key)) values.put(key, value); }); |
| | | snapshot.put("items", matrix.items()); snapshot.put("cycleNos", matrix.cycleNos()); snapshot.put("settings", new Settings(settings.fields(), values)); |
| | | snapshot.put("items", matrix.items()); snapshot.put("cycleNos", matrix.cycleNos()); snapshot.put("settings", new Settings(settings.fields(), values, |
| | | settings.chargeStepSelections() == null ? Map.of() : settings.chargeStepSelections())); |
| | | reports.append(id, request.baseVersionNo() + 1, snapshot.getString("name"), json(snapshot), userId, userName, "按最新来源生成:" + request.changeSummary().trim()); |
| | | return get(id, null); |
| | | } |
| | |
| | | }).toList(); |
| | | return new Item(item.source(), item.cycleNos(), rows, metrics(rows, item.cycleNos()), item.businessFields().stream() |
| | | .filter(value -> fields.stream().anyMatch(field -> "battery".equals(field.kind()) && (field.fieldDefinitionId() == null |
| | | ? field.key().equals(value.getFieldName()) : field.fieldDefinitionId().equals(value.getFieldDefinitionId())))).toList()); |
| | | ? field.key().equals(value.getFieldName()) : field.fieldDefinitionId().equals(value.getFieldDefinitionId())))).toList(), item.chargeSteps()); |
| | | }).toList(); |
| | | return new Matrix(frozen, matrix.cycleNos()); |
| | | } |
| | |
| | | throw new ServiceException("人工值不属于当前字段、测试电池或实际循环"); |
| | | } |
| | | |
| | | private void validateChargeSelections(Settings settings, List<Item> items) |
| | | { |
| | | Map<String, Integer> selections = settings.chargeStepSelections() == null ? Map.of() : settings.chargeStepSelections(); |
| | | for (Item item : items) for (int cycle : item.cycleNos()) { |
| | | String key = item.source().selection().testId() + "|" + item.source().selection().testBatteryId() + "|" + cycle; |
| | | List<ChargeStep> choices = item.chargeSteps().getOrDefault(cycle, List.of()); |
| | | Integer selected = selections.get(key); |
| | | if (choices.stream().filter(step -> isReliableCharge(step.stepType())).count() == 1) { |
| | | int expected = choices.stream().filter(step -> isReliableCharge(step.stepType())).findFirst().get().stepNo(); |
| | | if (selected != null && selected != expected) throw new ServiceException("充电工步选择与实际工步不一致"); |
| | | } else if (!choices.isEmpty()) { |
| | | if (selected == null) throw new ServiceException("请为无法自动识别的充电工步完成选择"); |
| | | if (choices.stream().noneMatch(step -> step.stepNo() == selected)) throw new ServiceException("所选充电工步不属于当前测试、电池和循环"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private Map<Integer, List<ChargeStep>> chargeSteps(List<Map<String, Object>> rows, TTestBattery battery, List<Integer> cycles) |
| | | { |
| | | Map<Integer, List<ChargeStep>> result = new LinkedHashMap<>(); |
| | | for (int cycle : cycles) result.put(cycle, rows == null ? List.of() : rows.stream() |
| | | .filter(row -> Objects.equals(battery.getBatteryNo(), row.get("batteryNo")) && cycleNo(row) == cycle) |
| | | .map(row -> new ChargeStep(number(row.get("stepNo")), String.valueOf(row.getOrDefault("stepName", "")), |
| | | String.valueOf(row.getOrDefault("stepType", "")), decimal(row.get("endVoltage")))) |
| | | // 无法可靠识别类型时保留该循环的全部实际工步,由操作员选择,不凭名称或电流猜测。 |
| | | .filter(step -> step.stepNo() > 0).toList()); |
| | | return result; |
| | | } |
| | | private boolean isReliableCharge(String type) { String value = type == null ? "" : type.trim().toLowerCase(); return value.equals("cc") || value.equals("charge") || value.equals("charging"); } |
| | | private int number(Object value) { return value instanceof Number n ? n.intValue() : 0; } |
| | | private BigDecimal decimal(Object value) { return value instanceof Number n ? new BigDecimal(n.toString()) : null; } |
| | | |
| | | private Set<String> valueKeys(List<FormationField> fields, List<Item> items) |
| | | { |
| | | Set<String> keys = new HashSet<>(); |