新威的工步名称按设备工步类型填方向名称
cap_step.step_type 实测 1 充电 / 2 放电 / 4 搁置,但工步记录与实时行此前把 stepName
硬编码成 null,「实时」「工步」两页的工步名称一列全是 -。现在按该类型解释出的方向名称
填写,与 22 所同词(充电/放电/搁置);未确认取值(实测 3/5/7/13)留空显示 -,不按电流
符号或容量出现在哪一组猜方向。step_type 仍原样保留设备给的数字。
方向名称词表统一放到 StepDirectionResolver.Direction.displayName(),推式来源的枚举改为
委托它,两处不再各写一套词;两条路径的分工随之明确为「来源给了类型就按类型解释,来源没
有类型列才按已核对的列语义判定」。见 ADR-0067 §4b 补记。
| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.battery.device.api.DeviceAdapter; |
| | | import com.battery.device.api.DeviceAdapterException; |
| | | import com.battery.device.api.StepDirectionResolver.Direction; |
| | | import com.battery.device.api.TestDataObservationAdapter; |
| | | import com.battery.device.api.model.AdapterCapabilities; |
| | | import com.battery.device.api.model.ChannelIdentity; |
| | |
| | | */ |
| | | private enum StepDirection |
| | | { |
| | | CHARGE("charge", "充电"), DISCHARGE("discharge", "放电"), REST("rest", "搁置"), UNKNOWN(null, null); |
| | | CHARGE("charge", Direction.CHARGE), DISCHARGE("discharge", Direction.DISCHARGE), |
| | | REST("rest", Direction.REST), UNKNOWN(null, Direction.UNKNOWN); |
| | | |
| | | private final String label; |
| | | private final String displayName; |
| | | private final Direction direction; |
| | | |
| | | StepDirection(String label, String displayName) { this.label = label; this.displayName = displayName; } |
| | | StepDirection(String label, Direction direction) { this.label = label; this.direction = direction; } |
| | | |
| | | /** 写进工步记录的类型:来源没给类型时交出的是判定出的方向名称,未知方向留空。 */ |
| | | String label() { return label; } |
| | | |
| | | /** |
| | | * 交出去的工步名称。这类来源的工步方案里只有「步_N」这种标号,没有描述性名称 |
| | | * (2026-10-09 现场核对 .pcj:<Step StepLabel="步_1"> + CtrlTypeName=Rest/Current); |
| | | * 而甲方软件那一列「状态」显示的就是充电/放电/搁置——所以用同一套词当工步名称, |
| | | * 值同样来自电流符号判定,**不是设备给的名称**(现场清单与 ADR-0067 都记了这一条)。 |
| | | * 交出去的工步名称:方向名称的词表与显示口径统一在 {@link Direction#displayName()}(甲方那一列的原词)。 |
| | | * 这类来源的工步方案里只有「步_N」这种标号,没有描述性名称(2026-10-09 现场核对 .pcj: |
| | | * {@code <Step StepLabel="步_1">} + CtrlTypeName=Rest/Current),名称是按电流符号判定出来的, |
| | | * **不是设备给的名称**;方向未知时留空。 |
| | | */ |
| | | String displayName() { return displayName; } |
| | | String displayName() { return direction.displayName(); } |
| | | |
| | | /** 该工步自身累计的单项值。搁置段实测两向都不增长,所以它的累计是 0 而不是缺失。 */ |
| | | BigDecimal own(BigDecimal charge, BigDecimal discharge) |
| | |
| | | /** 将已保存的实际工步类型解释为充电、放电、静置或未知,不依据测量值猜测。 */ |
| | | public interface StepDirectionResolver |
| | | { |
| | | enum Direction { CHARGE, DISCHARGE, REST, UNKNOWN } |
| | | enum Direction |
| | | { |
| | | CHARGE("充电"), DISCHARGE("放电"), REST("搁置"), UNKNOWN(null); |
| | | |
| | | private final String displayName; |
| | | |
| | | Direction(String displayName) { this.displayName = displayName; } |
| | | |
| | | /** |
| | | * 界面上显示的方向名称,用甲方「实时」页那一列的同一个词;工步名称填的就是它(ADR-0067 §4b)。 |
| | | * 方向未确认时为空:调用方不得用默认名把未知说成已知。 |
| | | */ |
| | | public String displayName() { return displayName; } |
| | | } |
| | | |
| | | /** 返回有明确协议依据的方向;未定义或混合类型保持未知。 */ |
| | | Direction resolve(String stepType); |
| | |
| | | |
| | | /** |
| | | * 返回该通道的工步统计。来源有工步层时读设备工步层并原样交出;来源没有工步层时由适配器 |
| | | * 从采样段推导(ADR-0067),此时记录里的方向是推导出的方向名称(见 {@link StepRecord})。 |
| | | * 从采样段推导(ADR-0067)。两种形态的记录里 {@code stepName} 都是**方向名称** |
| | | * ({@link com.battery.device.api.StepDirectionResolver.Direction#displayName()}):来源给出工步类型时按该类型解释, |
| | | * 来源没有类型列时按已核对的列语义判定;判定不出方向时留空,不写默认名。 |
| | | * 未结束的段不交出,也不推导来源根本没有的工步层。 |
| | | */ |
| | | List<StepRecord> stepRecords(ChannelIdentity channel); |
| | |
| | | |
| | | /** |
| | | * 一条设备工步层记录。来源给出工步类型时 {@code stepType} 原样保留(新威为数字枚举), |
| | | * 方向由调用方按该类型解释,适配器不按电流符号改写它。 |
| | | * 适配器不按电流符号改写它。 |
| | | * |
| | | * <p>来源**没有**工步类型列时(推式来源的厂商库里既没有工步层也没有循环汇总表,ADR-0067), |
| | | * 适配器交出的是按已核对列语义判定出的方向名称({@code charge} / {@code discharge} / {@code rest}), |
| | | * 判定不出来时留空;此时该工步的其余一切仍是设备采样行上的原文事实。 |
| | | * <p>{@code stepName} 是**方向名称**(`充电` / `放电` / `搁置`,ADR-0067 §4b):来源给出工步类型时 |
| | | * 按该类型解释(新威的 {@code 1}/{@code 2}/{@code 4}),来源没有类型列时(推式来源的厂商库里既没有 |
| | | * 工步层也没有循环汇总表,ADR-0067)按已核对列语义判定。方向判不出来时留空——**不是设备给的名称**, |
| | | * 界面据此标注来源,调用方不得用默认名把未知说成已知。 |
| | | * |
| | | * <p>来源没有工步层时,除方向名称外该工步的其余一切仍是设备采样行上的原文事实。 |
| | | * |
| | | * <p>{@code endVoltageV} 与 {@code endCurrentA} 是该工步结束时的设备读数。 |
| | | * {@code capacityAh} 与 {@code energyWh} 是该工步自身累计的单项值,只在方向已确认时给出; |
| | |
| | | * </ul> |
| | | * |
| | | * <p>单位换算按实测公式:电压与电流除以 10000(原单位 0.1 mV / 0.1 mA),容量与能量除以 36000000 |
| | | * (原单位 1e-4 C / 1e-4 J)。工步类型保留设备给出的数字,不按电流符号猜测方向; |
| | | * (原单位 1e-4 C / 1e-4 J)。工步类型保留设备给出的数字,不按电流符号猜测方向;工步名称由该类型 |
| | | * 经 {@link NewareStepDirectionResolver} 解释出的方向名称填写(未确认取值留空,ADR-0067 §4b); |
| | | * 内阻与温度的单位语义仍未确认,因此不对外暴露。 |
| | | */ |
| | | public final class NewareMysqlAdapter implements DeviceAdapter, TestDataObservationAdapter |
| | |
| | | } |
| | | |
| | | /** |
| | | * 一条工步记录。工步时间取设备给的 {@code test_time}(毫秒 → 秒),四组容量能量与中值电压 |
| | | * 放进扩展测量;单项容量只在方向已确认时给出。 |
| | | * 一条工步记录。工步名称是设备工步类型解释出的方向名称(类型未确认时留空);工步时间取设备给的 |
| | | * {@code test_time}(毫秒 → 秒),四组容量能量与中值电压放进扩展测量;单项容量只在方向已确认时给出。 |
| | | */ |
| | | private StepRecord stepRecord(long testId, StepGroup group) |
| | | { |
| | |
| | | Double capacityAh = capacity == null ? null : scaled(capacity, CAPACITY_ENERGY_DIVISOR); |
| | | Double energyWh = energy == null ? null : scaled(energy, CAPACITY_ENERGY_DIVISOR); |
| | | return new StepRecord(sourceRecordId(testId, "step", group.key().cycle() + ":" + group.key().stepId()), |
| | | group.key().cycle(), group.key().stepId(), null, last.stepType(), first.deviceTime(), |
| | | group.key().cycle(), group.key().stepId(), direction.displayName(), last.stepType(), first.deviceTime(), |
| | | last.deviceTime(), scaled(last.voltage(), VOLTAGE_CURRENT_DIVISOR), |
| | | scaled(last.current(), VOLTAGE_CURRENT_DIVISOR), capacityAh, energyWh, |
| | | last.deviceTimeRaw(), deviceZone.getId(), last.deviceTime(), measurements); |
| | |
| | | BigDecimal energy = charging ? row.energyCharge() : row.energyDischarge(); |
| | | return new CabinetRealtime(row.sequenceId(), row.deviceTime(), scaled(row.current(), VOLTAGE_CURRENT_DIVISOR), |
| | | scaled(row.voltage(), VOLTAGE_CURRENT_DIVISOR), scaled(capacity, CAPACITY_ENERGY_DIVISOR), |
| | | scaled(energy, CAPACITY_ENERGY_DIVISOR), row.cycle(), row.stepId(), null, row.stepType(), |
| | | scaled(energy, CAPACITY_ENERGY_DIVISOR), row.cycle(), row.stepId(), |
| | | // 工步名称填设备工步类型解释出的方向名称;类型未确认时留空,不用默认名冒充已知。 |
| | | directions.resolve(row.stepType()).displayName(), row.stepType(), |
| | | clock.instant(), row.deviceTimeRaw(), deviceZone.getId(), |
| | | row.testTimeMillis() == null ? null : row.testTimeMillis() / 1000, |
| | | "neware:" + row.testId() + ":" + row.sequenceId()); |
| | |
| | | assertEquals(3, latest.cycleNo()); |
| | | assertEquals(12, latest.stepNo()); |
| | | assertEquals("2", latest.stepType()); |
| | | // 工步名称由设备工步类型解释出的方向填写:2 = 放电。类型本身仍保留设备给的数字。 |
| | | assertEquals("放电", latest.stepName()); |
| | | assertEquals("neware:440:7435", latest.sourceRecordId()); |
| | | // 工步类型保留设备给出的数字,不按电流符号猜方向;同一秒的另一行靠工步与序号区分。 |
| | | // 同一秒的另一行靠工步与序号区分。 |
| | | assertEquals(9, rows.get(1).stepNo()); |
| | | } |
| | | |
| | |
| | | |
| | | StepRecord rest = step(steps, 0, 1); |
| | | assertEquals("4", rest.stepType()); |
| | | assertNull(rest.stepName()); |
| | | // 工步名称按设备工步类型解释出的方向:4 = 搁置。 |
| | | assertEquals("搁置", rest.stepName()); |
| | | assertEquals(Instant.parse("2026-09-28T01:00:00Z"), rest.startedAt()); |
| | | assertEquals(Instant.parse("2026-09-28T01:00:38Z"), rest.endedAt()); |
| | | assertEquals(1.3588, rest.endVoltageV(), 1e-9); |
| | |
| | | |
| | | StepRecord charge = step(steps, 0, 4); |
| | | assertEquals("1", charge.stepType()); |
| | | assertEquals("充电", charge.stepName()); |
| | | // 单项容量只在方向已确认时给出:充电工步取充电侧。 |
| | | assertEquals(119277040d / 36000000d, charge.capacityAh(), 1e-9); |
| | | assertEquals(195220576d / 36000000d, charge.energyWh(), 1e-9); |
| | |
| | | |
| | | StepRecord discharge = step(steps, 0, 6); |
| | | assertEquals("2", discharge.stepType()); |
| | | assertEquals("放电", discharge.stepName()); |
| | | assertEquals(68462032d / 36000000d, discharge.capacityAh(), 1e-9); |
| | | assertTrue(discharge.measurements().containsKey("dischargeMedianVoltage")); |
| | | // 四组容量能量都是设备事实:每一组都保留,不只保留方向匹配的那一组。 |
| | |
| | | List<StepRecord> steps = adapter(stepLayerDatabase()).stepRecords(CHANNEL); |
| | | StepRecord unknown = step(steps, 1, 3); |
| | | assertEquals("7", unknown.stepType()); |
| | | // 类型未确认时工步名称留空:不按电流符号也不按名称猜方向。 |
| | | assertNull(unknown.stepName()); |
| | | // 单项容量留空:方向未确认时不给出可归属的单值,但四组明细照原样保留供核对。 |
| | | assertNull(unknown.capacityAh()); |
| | | assertNull(unknown.energyWh()); |