diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/AlarmV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/AlarmV2016BlockParser.java index ca64e866..9b8d06ae 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/AlarmV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/AlarmV2016BlockParser.java @@ -4,6 +4,7 @@ import com.lingniu.ingest.protocol.gb32960.codec.InfoBlockParser; import com.lingniu.ingest.protocol.gb32960.codec.ValueDecoder; import com.lingniu.ingest.protocol.gb32960.model.InfoBlock; import com.lingniu.ingest.protocol.gb32960.model.ProtocolVersion; +// ProtocolVersion still used by version() override below import java.nio.ByteBuffer; import java.util.ArrayList; @@ -29,9 +30,8 @@ public final class AlarmV2016BlockParser implements InfoBlockParser { List motor = readFaultList(buf); List engine = readFaultList(buf); List other = readFaultList(buf); - return new InfoBlock.Alarm( - ProtocolVersion.V2016, maxLevel, generalFlag, - battery, motor, engine, other, null); + return new InfoBlock.Gb32960V2016.Alarm( + maxLevel, generalFlag, battery, motor, engine, other); } static List readFaultList(ByteBuffer buf) { diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/DriveMotorV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/DriveMotorV2016BlockParser.java index ffb8eab1..b6c0250c 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/DriveMotorV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/DriveMotorV2016BlockParser.java @@ -25,7 +25,7 @@ public final class DriveMotorV2016BlockParser implements InfoBlockParser { @Override public InfoBlock parse(ByteBuffer buf) { int count = buf.get() & 0xFF; - List motors = new ArrayList<>(count); + List motors = new ArrayList<>(count); for (int i = 0; i < count; i++) { Integer serialNo = ValueDecoder.u8(buf); Integer state = ValueDecoder.u8(buf); @@ -37,9 +37,9 @@ public final class DriveMotorV2016BlockParser implements InfoBlockParser { Integer motorTempC = ValueDecoder.tempOffset40(buf); Double ctrlVoltage = ValueDecoder.scaledU16(buf, 0.1); Double ctrlCurrent = ValueDecoder.scaledU16WithOffset(buf, 0.1, -1000.0); - motors.add(new InfoBlock.DriveMotor.Motor( + motors.add(new InfoBlock.Gb32960V2016.DriveMotor.Motor( serialNo, state, ctrlTempC, rpm, torque, motorTempC, ctrlVoltage, ctrlCurrent)); } - return new InfoBlock.DriveMotor(motors); + return new InfoBlock.Gb32960V2016.DriveMotor(motors); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/EngineV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/EngineV2016BlockParser.java index 6f453b5f..580857ae 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/EngineV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/EngineV2016BlockParser.java @@ -23,6 +23,6 @@ public final class EngineV2016BlockParser implements InfoBlockParser { Integer engineState = ValueDecoder.u8(buf); Integer crankshaftRpm = ValueDecoder.u16(buf); Double fuelRate = ValueDecoder.scaledU16(buf, 0.01); - return new InfoBlock.Engine(engineState, crankshaftRpm, fuelRate); + return new InfoBlock.Gb32960V2016.Engine(engineState, crankshaftRpm, fuelRate); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/ExtremeValueV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/ExtremeValueV2016BlockParser.java index f7a92f6b..c5361903 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/ExtremeValueV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/ExtremeValueV2016BlockParser.java @@ -31,7 +31,7 @@ public final class ExtremeValueV2016BlockParser implements InfoBlockParser { Integer minTSub = ValueDecoder.u8(buf); Integer minTProbe = ValueDecoder.u8(buf); Integer minT = ValueDecoder.tempOffset40(buf); - return new InfoBlock.Extreme( + return new InfoBlock.Gb32960V2016.Extreme( maxVSub, maxVCell, maxV, minVSub, minVCell, minV, maxTSub, maxTProbe, maxT, diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/FuelCellV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/FuelCellV2016BlockParser.java index 74bbdf2c..527657d0 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/FuelCellV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/FuelCellV2016BlockParser.java @@ -44,7 +44,7 @@ public final class FuelCellV2016BlockParser implements InfoBlockParser { Integer maxHydrogenPressureProbeId = ValueDecoder.u8(buf); Integer hvDcDcStatus = ValueDecoder.u8(buf); - return new InfoBlock.FuelCellV2016( + return new InfoBlock.Gb32960V2016.FuelCell( fcVoltage, fcCurrent, consumption, probes, maxHydrogenTempC, maxHydrogenTempProbeId, maxHydrogenConcentration, maxHydrogenConcentrationProbeId, diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/PositionV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/PositionV2016BlockParser.java index b0228618..a543f7fb 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/PositionV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/PositionV2016BlockParser.java @@ -27,6 +27,6 @@ public final class PositionV2016BlockParser implements InfoBlockParser { double latitude = latRaw / 1_000_000.0; if ((statusFlag & 0b100) != 0) longitude = -longitude; if ((statusFlag & 0b010) != 0) latitude = -latitude; - return new InfoBlock.Position(statusFlag, null, longitude, latitude); + return new InfoBlock.Gb32960V2016.Position(statusFlag, longitude, latitude); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/TemperatureV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/TemperatureV2016BlockParser.java index 4a8d742a..375bc7b3 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/TemperatureV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/TemperatureV2016BlockParser.java @@ -34,6 +34,6 @@ public final class TemperatureV2016BlockParser implements InfoBlockParser { } if (maxT == Integer.MIN_VALUE) maxT = 0; if (minT == Integer.MAX_VALUE) minT = 0; - return new InfoBlock.Temperature(subCount, maxT, minT); + return new InfoBlock.Gb32960V2016.Temperature(subCount, maxT, minT); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VehicleV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VehicleV2016BlockParser.java index 941c0140..df2558bc 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VehicleV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VehicleV2016BlockParser.java @@ -35,8 +35,8 @@ public final class VehicleV2016BlockParser implements InfoBlockParser { Integer insulation = ValueDecoder.u16(buf); Integer accelPedal = ValueDecoder.u8(buf); Integer brakePedal = ValueDecoder.u8(buf); - return new InfoBlock.Vehicle(vehicleState, chargingState, runningMode, speedKmh, mileageKm, - totalVoltageV, totalCurrentA, soc, dcDcStatus, gearRaw, insulation, - accelPedal, brakePedal); + return new InfoBlock.Gb32960V2016.Vehicle(vehicleState, chargingState, runningMode, + speedKmh, mileageKm, totalVoltageV, totalCurrentA, soc, dcDcStatus, gearRaw, + insulation, accelPedal, brakePedal); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VoltageV2016BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VoltageV2016BlockParser.java index 33733088..e6d5fdf8 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VoltageV2016BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2016/VoltageV2016BlockParser.java @@ -44,6 +44,6 @@ public final class VoltageV2016BlockParser implements InfoBlockParser { } if (maxCell == Double.MIN_VALUE) maxCell = 0; if (minCell == Double.MAX_VALUE) minCell = 0; - return new InfoBlock.Voltage(subCount, maxCell, minCell, totalV); + return new InfoBlock.Gb32960V2016.Voltage(subCount, maxCell, minCell, totalV); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/AlarmV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/AlarmV2025BlockParser.java index 6fce0e27..8a3b0b78 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/AlarmV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/AlarmV2025BlockParser.java @@ -29,16 +29,15 @@ public final class AlarmV2025BlockParser implements InfoBlockParser { List other = readFaultList(buf); int generalAlarmCount = buf.hasRemaining() ? (buf.get() & 0xFF) : 0; - List levels = new ArrayList<>(generalAlarmCount); + List levels = new ArrayList<>(generalAlarmCount); for (int i = 0; i < generalAlarmCount && buf.remaining() >= 2; i++) { int bit = buf.get() & 0xFF; int level = buf.get() & 0xFF; - levels.add(new InfoBlock.Alarm.GeneralAlarmEntry(bit, level)); + levels.add(new InfoBlock.Gb32960V2025.Alarm.GeneralAlarmEntry(bit, level)); } - return new InfoBlock.Alarm( - ProtocolVersion.V2025, maxLevel, generalFlag, - battery, motor, engine, other, levels); + return new InfoBlock.Gb32960V2025.Alarm( + maxLevel, generalFlag, battery, motor, engine, other, levels); } private static List readFaultList(ByteBuffer buf) { diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/BatteryTemperatureV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/BatteryTemperatureV2025BlockParser.java index 6bf7ec40..002a64c8 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/BatteryTemperatureV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/BatteryTemperatureV2025BlockParser.java @@ -21,7 +21,7 @@ public final class BatteryTemperatureV2025BlockParser implements InfoBlockParser @Override public InfoBlock parse(ByteBuffer buf) { int subCount = buf.get() & 0xFF; - List packs = new ArrayList<>(subCount); + List packs = new ArrayList<>(subCount); for (int i = 0; i < subCount; i++) { int packNo = buf.get() & 0xFF; int probeCount = buf.getShort() & 0xFFFF; @@ -30,8 +30,8 @@ public final class BatteryTemperatureV2025BlockParser implements InfoBlockParser for (int k = 0; k < safeCount; k++) { temps.add((buf.get() & 0xFF) - 40); } - packs.add(new InfoBlock.BatteryTemperature.Pack(packNo, probeCount, temps)); + packs.add(new InfoBlock.Gb32960V2025.BatteryTemperature.Pack(packNo, probeCount, temps)); } - return new InfoBlock.BatteryTemperature(subCount, packs); + return new InfoBlock.Gb32960V2025.BatteryTemperature(subCount, packs); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/DriveMotorV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/DriveMotorV2025BlockParser.java index 9a7abec9..bcf49e03 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/DriveMotorV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/DriveMotorV2025BlockParser.java @@ -27,7 +27,7 @@ public final class DriveMotorV2025BlockParser implements InfoBlockParser { @Override public InfoBlock parse(ByteBuffer buf) { int count = buf.get() & 0xFF; - List motors = new ArrayList<>(count); + List motors = new ArrayList<>(count); for (int i = 0; i < count; i++) { Integer serialNo = ValueDecoder.u8(buf); Integer state = ValueDecoder.u8(buf); @@ -39,9 +39,9 @@ public final class DriveMotorV2025BlockParser implements InfoBlockParser { Integer motorTempC = ValueDecoder.tempOffset40(buf); Double ctrlVoltage = ValueDecoder.scaledU16(buf, 0.1); Double ctrlCurrent = ValueDecoder.scaledU16WithOffset(buf, 0.1, -1000.0); - motors.add(new InfoBlock.DriveMotor.Motor( + motors.add(new InfoBlock.Gb32960V2025.DriveMotor.Motor( serialNo, state, ctrlTempC, rpm, torque, motorTempC, ctrlVoltage, ctrlCurrent)); } - return new InfoBlock.DriveMotor(motors); + return new InfoBlock.Gb32960V2025.DriveMotor(motors); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/EngineV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/EngineV2025BlockParser.java index 8f17f8a2..48661477 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/EngineV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/EngineV2025BlockParser.java @@ -22,6 +22,6 @@ public final class EngineV2025BlockParser implements InfoBlockParser { Integer engineState = ValueDecoder.u8(buf); Integer crankshaftRpm = ValueDecoder.u16(buf); Double fuelRate = ValueDecoder.scaledU16(buf, 0.01); - return new InfoBlock.Engine(engineState, crankshaftRpm, fuelRate); + return new InfoBlock.Gb32960V2025.Engine(engineState, crankshaftRpm, fuelRate); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellStackV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellStackV2025BlockParser.java index 32aecb90..64d42a21 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellStackV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellStackV2025BlockParser.java @@ -23,7 +23,7 @@ public final class FuelCellStackV2025BlockParser implements InfoBlockParser { @Override public InfoBlock parse(ByteBuffer buf) { int stackCount = buf.get() & 0xFF; - List stacks = new ArrayList<>(stackCount); + List stacks = new ArrayList<>(stackCount); for (int i = 0; i < stackCount; i++) { int stackNo = buf.get() & 0xFF; Double voltage = ValueDecoder.scaledU16(buf, 0.1); @@ -37,9 +37,9 @@ public final class FuelCellStackV2025BlockParser implements InfoBlockParser { for (int k = 0; k < safeCount; k++) { temps.add((buf.get() & 0xFF) - 40); } - stacks.add(new InfoBlock.FuelCellStack.Stack( + stacks.add(new InfoBlock.Gb32960V2025.FuelCellStack.Stack( stackNo, voltage, current, h2InletPressure, airInletPressure, airInletTemp, temps)); } - return new InfoBlock.FuelCellStack(stackCount, stacks); + return new InfoBlock.Gb32960V2025.FuelCellStack(stackCount, stacks); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellV2025BlockParser.java index 9adab4d8..72edbd76 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/FuelCellV2025BlockParser.java @@ -27,7 +27,7 @@ public final class FuelCellV2025BlockParser implements InfoBlockParser { Integer hvDcDcStatus = ValueDecoder.u8(buf); Integer remainingH2Percent = ValueDecoder.u8(buf); Integer hvDcDcControllerTempC = ValueDecoder.tempOffset40(buf); - return new InfoBlock.FuelCellV2025( + return new InfoBlock.Gb32960V2025.FuelCell( maxTempC, maxTempProbeId, maxConcentration, maxConcentrationProbeId, maxPressure, maxPressureProbeId, diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/MinParallelVoltageV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/MinParallelVoltageV2025BlockParser.java index 13db9676..665e67f1 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/MinParallelVoltageV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/MinParallelVoltageV2025BlockParser.java @@ -22,7 +22,7 @@ public final class MinParallelVoltageV2025BlockParser implements InfoBlockParser @Override public InfoBlock parse(ByteBuffer buf) { int subCount = buf.get() & 0xFF; - List packs = new ArrayList<>(subCount); + List packs = new ArrayList<>(subCount); for (int i = 0; i < subCount; i++) { int packNo = buf.get() & 0xFF; Double voltage = ValueDecoder.scaledU16(buf, 0.1); @@ -33,9 +33,9 @@ public final class MinParallelVoltageV2025BlockParser implements InfoBlockParser for (int k = 0; k < safeCount; k++) { frameVoltages.add((buf.getShort() & 0xFFFF) * 0.001); } - packs.add(new InfoBlock.MinParallelVoltage.Pack( + packs.add(new InfoBlock.Gb32960V2025.MinParallelVoltage.Pack( packNo, voltage, current, totalUnits, frameVoltages)); } - return new InfoBlock.MinParallelVoltage(subCount, packs); + return new InfoBlock.Gb32960V2025.MinParallelVoltage(subCount, packs); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/PositionV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/PositionV2025BlockParser.java index 3611f85f..d6a65c84 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/PositionV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/PositionV2025BlockParser.java @@ -29,6 +29,6 @@ public final class PositionV2025BlockParser implements InfoBlockParser { double latitude = latRaw / 1_000_000.0; if ((statusFlag & 0b100) != 0) longitude = -longitude; if ((statusFlag & 0b010) != 0) latitude = -latitude; - return new InfoBlock.Position(statusFlag, coordSystem, longitude, latitude); + return new InfoBlock.Gb32960V2025.Position(statusFlag, coordSystem, longitude, latitude); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorExtremeV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorExtremeV2025BlockParser.java index cd0f2b83..657a825c 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorExtremeV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorExtremeV2025BlockParser.java @@ -30,7 +30,7 @@ public final class SuperCapacitorExtremeV2025BlockParser implements InfoBlockPar Integer minTSys = ValueDecoder.u8(buf); Integer minTProbe = ValueDecoder.u16(buf); Integer minT = ValueDecoder.tempOffset40(buf); - return new InfoBlock.SuperCapacitorExtreme( + return new InfoBlock.Gb32960V2025.SuperCapacitorExtreme( maxVSys, maxVCell, maxV, minVSys, minVCell, minV, maxTSys, maxTProbe, maxT, diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorV2025BlockParser.java index b6411cf1..b1e65c07 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/SuperCapacitorV2025BlockParser.java @@ -35,6 +35,6 @@ public final class SuperCapacitorV2025BlockParser implements InfoBlockParser { for (int i = 0; i < safeProbeCount; i++) { temps.add((buf.get() & 0xFF) - 40); } - return new InfoBlock.SuperCapacitor(systemNo, totalVoltage, totalCurrent, cells, temps); + return new InfoBlock.Gb32960V2025.SuperCapacitor(systemNo, totalVoltage, totalCurrent, cells, temps); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/VehicleV2025BlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/VehicleV2025BlockParser.java index 5721bb71..d36348e6 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/VehicleV2025BlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/v2025/VehicleV2025BlockParser.java @@ -30,8 +30,8 @@ public final class VehicleV2025BlockParser implements InfoBlockParser { Integer dcDcStatus = ValueDecoder.u8(buf); Integer gearRaw = ValueDecoder.u8raw(buf); Integer insulation = ValueDecoder.u16(buf); - return new InfoBlock.Vehicle(vehicleState, chargingState, runningMode, speedKmh, mileageKm, - totalVoltageV, totalCurrentA, soc, dcDcStatus, gearRaw, insulation, - null, null); + return new InfoBlock.Gb32960V2025.Vehicle(vehicleState, chargingState, runningMode, + speedKmh, mileageKm, totalVoltageV, totalCurrentA, soc, dcDcStatus, gearRaw, + insulation); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAirConditionerBlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAirConditionerBlockParser.java index 68a1c18b..67a822d9 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAirConditionerBlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAirConditionerBlockParser.java @@ -29,6 +29,6 @@ public final class GdFcAirConditionerBlockParser implements InfoBlockParser { int rawPower = buf.getShort() & 0xFFFF; Integer power = (rawPower == 0xFFFE || rawPower == 0xFFFF) ? null : rawPower - 5; Double inputV = ValueDecoder.scaledU16(buf, 0.1); - return new InfoBlock.GdFcAirConditioner(status, power, inputV); + return new InfoBlock.GuangdongFc.AirConditioner(status, power, inputV); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAuxiliaryBlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAuxiliaryBlockParser.java index 09dd0fd8..919a6ee9 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAuxiliaryBlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcAuxiliaryBlockParser.java @@ -36,7 +36,7 @@ public final class GdFcAuxiliaryBlockParser implements InfoBlockParser { @Override public InfoBlock parse(ByteBuffer buf) { int count = buf.get() & 0xFF; - List subs = new ArrayList<>(count); + List subs = new ArrayList<>(count); for (int i = 0; i < count; i++) { Double airCompV = ValueDecoder.scaledU16(buf, 0.1); Integer airCompP = readPowerOffsetMinus5(buf); @@ -46,10 +46,10 @@ public final class GdFcAuxiliaryBlockParser implements InfoBlockParser { Double ptcV = ValueDecoder.scaledU16(buf, 0.1); Integer ptcP = readPowerOffsetMinus5(buf); Double lvBattV = ValueDecoder.scaledU16(buf, 0.1); - subs.add(new InfoBlock.GdFcAuxiliary.Subsystem( + subs.add(new InfoBlock.GuangdongFc.Auxiliary.Subsystem( airCompV, airCompP, h2PumpV, h2PumpP, waterPumpV, ptcV, ptcP, lvBattV)); } - return new InfoBlock.GdFcAuxiliary(count, subs); + return new InfoBlock.GuangdongFc.Auxiliary(count, subs); } /** 功率字段:raw(2B) - 5,0xFFFE/0xFFFF 异常/无效。 */ diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDcDcBlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDcDcBlockParser.java index 3d841392..71f7c170 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDcDcBlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDcDcBlockParser.java @@ -32,6 +32,6 @@ public final class GdFcDcDcBlockParser implements InfoBlockParser { Double outV = ValueDecoder.scaledU16(buf, 0.1); Double outC = ValueDecoder.scaledU16(buf, 0.1); Integer ctrlTemp = ValueDecoder.tempOffset40(buf); - return new InfoBlock.GdFcDcDc(inV, inC, outV, outC, ctrlTemp); + return new InfoBlock.GuangdongFc.DcDc(inV, inC, outV, outC, ctrlTemp); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDemoExtensionBlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDemoExtensionBlockParser.java index bb5a7655..8457d615 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDemoExtensionBlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcDemoExtensionBlockParser.java @@ -42,6 +42,6 @@ public final class GdFcDemoExtensionBlockParser implements InfoBlockParser { org.slf4j.LoggerFactory.getLogger(GdFcDemoExtensionBlockParser.class) .debug("[gb32960/gd-fc/0x80] declaredLen={} (expected 9), parsed by fixed layout", declaredLen); } - return new InfoBlock.GdFcDemoExtension(stackTemp, acV, acC, h2V, h2C); + return new InfoBlock.GuangdongFc.DemoExtension(stackTemp, acV, acC, h2V, h2C); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcStackBlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcStackBlockParser.java index 7c8caaf5..d597897e 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcStackBlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcStackBlockParser.java @@ -45,7 +45,7 @@ public final class GdFcStackBlockParser implements InfoBlockParser { @Override public InfoBlock parse(ByteBuffer buf) { int stackCount = buf.get() & 0xFF; - List stacks = new ArrayList<>(stackCount); + List stacks = new ArrayList<>(stackCount); for (int i = 0; i < stackCount; i++) { Integer workState = ValueDecoder.u8raw(buf); Integer waterTemp = ValueDecoder.tempOffset40(buf); @@ -65,11 +65,11 @@ public final class GdFcStackBlockParser implements InfoBlockParser { for (int k = 0; k < safe; k++) { frameVoltages.add((buf.getShort() & 0xFFFF) * 0.001); } - stacks.add(new InfoBlock.GdFcStack.Stack( + stacks.add(new InfoBlock.GuangdongFc.Stack.Item( workState, waterTemp, h2InletP, airInletP, airInletTemp, maxId, minId, maxV, minV, avgV, cellCount, frameStart, frameCells, frameVoltages)); } - return new InfoBlock.GdFcStack(stackCount, stacks); + return new InfoBlock.GuangdongFc.Stack(stackCount, stacks); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcVehicleInfoBlockParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcVehicleInfoBlockParser.java index 955bed01..50ffec99 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcVehicleInfoBlockParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcVehicleInfoBlockParser.java @@ -31,6 +31,6 @@ public final class GdFcVehicleInfoBlockParser implements InfoBlockParser { Integer tempC = (rawTemp == 0xFFFE || rawTemp == 0xFFFF) ? null : rawTemp - 40; Double pressure = ValueDecoder.scaledU16WithOffset(buf, 0.1, -100.0); Double h2Mass = ValueDecoder.scaledU16(buf, 0.1); - return new InfoBlock.GdFcVehicleInfo(collision, tempC, pressure, h2Mass); + return new InfoBlock.GuangdongFc.VehicleInfo(collision, tempC, pressure, h2Mass); } } diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java index ce1a0a65..9576af0f 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/mapper/Gb32960EventMapper.java @@ -102,11 +102,18 @@ public final class Gb32960EventMapper implements EventMapper { Map meta, Instant eventTime, Instant ingestTime, List out) { - InfoBlock.Vehicle v = message.findBlock(InfoBlock.Vehicle.class).orElse(null); - InfoBlock.Position p = message.findBlock(InfoBlock.Position.class).orElse(null); - InfoBlock.FuelCellV2016 fc16 = message.findBlock(InfoBlock.FuelCellV2016.class).orElse(null); - InfoBlock.FuelCellV2025 fc25 = message.findBlock(InfoBlock.FuelCellV2025.class).orElse(null); - InfoBlock.Alarm alarm = message.findBlock(InfoBlock.Alarm.class).orElse(null); + // 整车数据:先查 V2016,再 V2025;每帧只会命中其一,extracted 出来变成统一 + // 视图(VehicleView)供后面 payload 构造使用 + VehicleView v = extractVehicleView(message); + // 位置数据:同样两版本都查 + PositionView p = extractPositionView(message); + // 燃料电池:V2016 字段更全(带电压/电流),V2025 字段更少 + InfoBlock.Gb32960V2016.FuelCell fc16 = + message.findBlock(InfoBlock.Gb32960V2016.FuelCell.class).orElse(null); + InfoBlock.Gb32960V2025.FuelCell fc25 = + message.findBlock(InfoBlock.Gb32960V2025.FuelCell.class).orElse(null); + // 报警 + AlarmView alarm = extractAlarmView(message); if (v != null) { Double fcVoltageV = fc16 != null ? fc16.fcVoltageV() : null; @@ -119,25 +126,25 @@ public final class Gb32960EventMapper implements EventMapper { ? fc25.remainingHydrogenPercent().doubleValue() : null; RealtimePayload payload = new RealtimePayload( - v.speedKmh(), - v.totalMileageKm(), - v.socPercent() != null ? v.socPercent().doubleValue() : null, - v.totalVoltageV(), - v.totalCurrentA(), + v.speedKmh, + v.totalMileageKm, + v.socPercent != null ? v.socPercent.doubleValue() : null, + v.totalVoltageV, + v.totalCurrentA, fcVoltageV, fcCurrentA, fcTempC, hydrogenRemaining, hydrogenHighPressure, null, - mapVehicleState(v.vehicleState()), - mapChargingState(v.chargingState()), - mapRunningMode(v.runningMode()), - extractGearLevel(v.gearRaw()), - v.acceleratorPedal() != null ? v.acceleratorPedal().doubleValue() : null, - v.brakePedal() != null ? v.brakePedal().doubleValue() : null, - p != null ? p.longitude() : null, - p != null ? p.latitude() : null, + mapVehicleState(v.vehicleState), + mapChargingState(v.chargingState), + mapRunningMode(v.runningMode), + extractGearLevel(v.gearRaw), + v.acceleratorPedal != null ? v.acceleratorPedal.doubleValue() : null, + v.brakePedal != null ? v.brakePedal.doubleValue() : null, + p != null ? p.longitude : null, + p != null ? p.latitude : null, null, null, null, @@ -149,31 +156,31 @@ public final class Gb32960EventMapper implements EventMapper { } if (p != null) { LocationPayload loc = new LocationPayload( - p.longitude(), p.latitude(), 0.0, - v != null && v.speedKmh() != null ? v.speedKmh() : 0.0, - 0.0, 0, p.statusFlag()); + p.longitude, p.latitude, 0.0, + v != null && v.speedKmh != null ? v.speedKmh : 0.0, + 0.0, 0, p.statusFlag); out.add(new VehicleEvent.Location( UUID.randomUUID().toString(), header.vin(), ProtocolId.GB32960, eventTime, ingestTime, null, meta, loc)); } - if (alarm != null && alarm.maxLevel() != null && alarm.maxLevel() > 0) { + if (alarm != null && alarm.maxLevel != null && alarm.maxLevel > 0) { List faultCodes = new ArrayList<>(); - addFaultCodes(faultCodes, "BAT", alarm.batteryFaults()); - addFaultCodes(faultCodes, "MOT", alarm.motorFaults()); - addFaultCodes(faultCodes, "ENG", alarm.engineFaults()); - addFaultCodes(faultCodes, "OTH", alarm.otherFaults()); - Set activeBits = GeneralAlarmFlagBit.parse(alarm.generalAlarmFlag()).stream() + addFaultCodes(faultCodes, "BAT", alarm.batteryFaults); + addFaultCodes(faultCodes, "MOT", alarm.motorFaults); + addFaultCodes(faultCodes, "ENG", alarm.engineFaults); + addFaultCodes(faultCodes, "OTH", alarm.otherFaults); + Set activeBits = GeneralAlarmFlagBit.parse(alarm.generalAlarmFlag).stream() .map(Enum::name) .collect(Collectors.toCollection(java.util.LinkedHashSet::new)); AlarmPayload ap = new AlarmPayload( - mapAlarmLevel(alarm.maxLevel()), - (int) (alarm.generalAlarmFlag() & 0xFFFF), + mapAlarmLevel(alarm.maxLevel), + (int) (alarm.generalAlarmFlag & 0xFFFF), "GB32960_ALARM", faultCodes, activeBits, - p != null ? p.longitude() : null, - p != null ? p.latitude() : null); + p != null ? p.longitude : null, + p != null ? p.latitude : null); out.add(new VehicleEvent.Alarm( UUID.randomUUID().toString(), header.vin(), ProtocolId.GB32960, eventTime, ingestTime, @@ -181,6 +188,61 @@ public final class Gb32960EventMapper implements EventMapper { } } + /** 跨版本的整车数据视图,把 V2016 的踏板字段(V2025 没有)放在一起。 */ + private record VehicleView( + Integer vehicleState, Integer chargingState, Integer runningMode, + Double speedKmh, Double totalMileageKm, Double totalVoltageV, Double totalCurrentA, + Integer socPercent, Integer gearRaw, + Integer acceleratorPedal, Integer brakePedal) {} + + private static VehicleView extractVehicleView(Gb32960Message m) { + var v16 = m.findBlock(InfoBlock.Gb32960V2016.Vehicle.class).orElse(null); + if (v16 != null) { + return new VehicleView(v16.vehicleState(), v16.chargingState(), v16.runningMode(), + v16.speedKmh(), v16.totalMileageKm(), v16.totalVoltageV(), v16.totalCurrentA(), + v16.socPercent(), v16.gearRaw(), + v16.acceleratorPedal(), v16.brakePedal()); + } + var v25 = m.findBlock(InfoBlock.Gb32960V2025.Vehicle.class).orElse(null); + if (v25 != null) { + return new VehicleView(v25.vehicleState(), v25.chargingState(), v25.runningMode(), + v25.speedKmh(), v25.totalMileageKm(), v25.totalVoltageV(), v25.totalCurrentA(), + v25.socPercent(), v25.gearRaw(), + null, null); + } + return null; + } + + /** 跨版本的位置数据视图。 */ + private record PositionView(int statusFlag, double longitude, double latitude) {} + + private static PositionView extractPositionView(Gb32960Message m) { + var p16 = m.findBlock(InfoBlock.Gb32960V2016.Position.class).orElse(null); + if (p16 != null) return new PositionView(p16.statusFlag(), p16.longitude(), p16.latitude()); + var p25 = m.findBlock(InfoBlock.Gb32960V2025.Position.class).orElse(null); + if (p25 != null) return new PositionView(p25.statusFlag(), p25.longitude(), p25.latitude()); + return null; + } + + /** 跨版本的报警视图(仅暴露公共字段,2025 的 generalAlarmLevels 暂未上送下游)。 */ + private record AlarmView(Integer maxLevel, long generalAlarmFlag, + List batteryFaults, List motorFaults, + List engineFaults, List otherFaults) {} + + private static AlarmView extractAlarmView(Gb32960Message m) { + var a16 = m.findBlock(InfoBlock.Gb32960V2016.Alarm.class).orElse(null); + if (a16 != null) { + return new AlarmView(a16.maxLevel(), a16.generalAlarmFlag(), + a16.batteryFaults(), a16.motorFaults(), a16.engineFaults(), a16.otherFaults()); + } + var a25 = m.findBlock(InfoBlock.Gb32960V2025.Alarm.class).orElse(null); + if (a25 != null) { + return new AlarmView(a25.maxLevel(), a25.generalAlarmFlag(), + a25.batteryFaults(), a25.motorFaults(), a25.engineFaults(), a25.otherFaults()); + } + return null; + } + /** 挡位字节按附录 A.1 解析:低 4 位为挡位值 0000-1111。 */ private static Integer extractGearLevel(Integer gearRaw) { if (gearRaw == null) return null; diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/model/InfoBlock.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/model/InfoBlock.java index a36ee03a..138e02bc 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/model/InfoBlock.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/model/InfoBlock.java @@ -3,498 +3,429 @@ package com.lingniu.ingest.protocol.gb32960.model; import java.util.List; /** - * 已解析的信息体基接口。每种语义对应一种 record 实现。 + * 已解析的信息体基接口。按 {@code <协议+版本>.<数据类型>} 严格分组: * - *

规范来源:GB/T 32960.3-2016 附录 B 表 B.10-B.17,GB/T 32960.3-2025 §7.2.4 表 10-27。 - * 字段命名使用规范中的中文直译以便对照查阅。 + *

    + *
  • {@link Gb32960V2016} —— GB/T 32960.3-2016 附录 B 表 B.10~B.20 + *
  • {@link Gb32960V2025} —— GB/T 32960.3-2025 §7.2.4 表 10~27 + *
  • {@link GuangdongFc} —— 广东燃料电池汽车示范应用城市群综合监管平台规范 v1.0 + *
  • {@link Raw} —— 未实现 parser 的原始字节兜底 + *
+ * + *

哪怕同一个数据类型在两个版本字段一致(如 Engine 5 字节固定布局),也按规则 + * 复制成两份 record,绝不跨版本共用 record 类型,以便消费方在 instanceof / switch + * 时能严格区分数据来源。 * *

异常值约定: *

    - *
  • 单字节:{@code 0xFE} 异常、{@code 0xFF} 无效 - *
  • 双字节:{@code 0xFFFE} 异常、{@code 0xFFFF} 无效 - *
  • 四字节:{@code 0xFFFFFFFE} 异常、{@code 0xFFFFFFFF} 无效 + *
  • 单字节:{@code 0xFE} 异常 / {@code 0xFF} 无效 + *
  • 双字节:{@code 0xFFFE} 异常 / {@code 0xFFFF} 无效 + *
  • 四字节:{@code 0xFFFFFFFE} 异常 / {@code 0xFFFFFFFF} 无效 *
- * 对外 record 使用 {@link Double}/{@link Integer} 等装箱类型,异常或无效时设为 {@code null}。 + * 对外 record 使用 {@link Double} / {@link Integer} 等装箱类型,异常或无效时设为 + * {@code null}。 */ public sealed interface InfoBlock - permits InfoBlock.Vehicle, - InfoBlock.DriveMotor, - InfoBlock.FuelCellV2016, - InfoBlock.FuelCellV2025, - InfoBlock.Engine, - InfoBlock.Position, - InfoBlock.Extreme, - InfoBlock.Alarm, - InfoBlock.Voltage, - InfoBlock.Temperature, - InfoBlock.MinParallelVoltage, - InfoBlock.BatteryTemperature, - InfoBlock.FuelCellStack, - InfoBlock.SuperCapacitor, - InfoBlock.SuperCapacitorExtreme, - InfoBlock.GdFcStack, - InfoBlock.GdFcAuxiliary, - InfoBlock.GdFcDcDc, - InfoBlock.GdFcAirConditioner, - InfoBlock.GdFcVehicleInfo, - InfoBlock.GdFcDemoExtension, + permits InfoBlock.Gb32960V2016, + InfoBlock.Gb32960V2025, + InfoBlock.GuangdongFc, InfoBlock.Raw { InfoBlockType type(); // ======================================================================== - // 0x01 整车数据 —— 2016/2025 共享(表 10) + // GB/T 32960.3-2016 // ======================================================================== - /** - * 整车数据。固定 21 字节(2025 版)/ 20 字节(2016 版,无 brake)。 - * - * @param vehicleState 车辆状态:0x01 启动 / 0x02 熄火 / 0x03 其他 - * @param chargingState 充电状态:0x01 停车充电 / 0x02 行驶充电 / 0x03 未充电 / 0x04 充电完成 - * @param runningMode 运行模式:0x01 纯电 / 0x02 混动 / 0x03 燃油 - * @param speedKmh 车速 km/h,0~500,最小计量 0.1 km/h - * @param totalMileageKm 累计里程 km,0~999999.9,最小计量 0.1 km - * @param totalVoltageV 总电压 V,0~6000,最小计量 0.1 V - * @param totalCurrentA 总电流 A,-3000~+3000(原始值偏移 3000 A),最小计量 0.1 A - * @param socPercent SOC 百分比,0~100 - * @param dcDcStatus DC-DC 状态:0x01 工作 / 0x02 断开 - * @param gearRaw 挡位原始字节,按附录 A.1 解析 - * @param insulationResistanceKohm 高压对地绝缘电阻 kΩ,0~60000 - * @param acceleratorPedal 加速踏板行程值 0~100(仅 2016 版) - * @param brakePedal 制动踏板状态 0~100(仅 2016 版) - */ - record Vehicle( - Integer vehicleState, - Integer chargingState, - Integer runningMode, - Double speedKmh, - Double totalMileageKm, - Double totalVoltageV, - Double totalCurrentA, - Integer socPercent, - Integer dcDcStatus, - Integer gearRaw, - Integer insulationResistanceKohm, - Integer acceleratorPedal, - Integer brakePedal - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.VEHICLE; } - } + /** GB/T 32960.3-2016 附录 B 定义的全部信息体类型。 */ + sealed interface Gb32960V2016 extends InfoBlock + permits Gb32960V2016.Vehicle, + Gb32960V2016.DriveMotor, + Gb32960V2016.FuelCell, + Gb32960V2016.Engine, + Gb32960V2016.Position, + Gb32960V2016.Extreme, + Gb32960V2016.Alarm, + Gb32960V2016.Voltage, + Gb32960V2016.Temperature { - // ======================================================================== - // 0x02 驱动电机数据 —— 表 15/16 - // ======================================================================== + /** 0x01 整车数据。固定 20 字节(含加速/制动踏板)。 */ + record Vehicle( + Integer vehicleState, + Integer chargingState, + Integer runningMode, + Double speedKmh, + Double totalMileageKm, + Double totalVoltageV, + Double totalCurrentA, + Integer socPercent, + Integer dcDcStatus, + Integer gearRaw, + Integer insulationResistanceKohm, + Integer acceleratorPedal, + Integer brakePedal + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.VEHICLE; } + } - /** 驱动电机数据。变长:1 字节电机个数 + N × 电机信息。 */ - record DriveMotor(List motors) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.DRIVE_MOTOR; } + /** 0x02 驱动电机数据。变长。每电机 12 字节(rpm 偏移 20000,torque 2 字节偏移 20000)。 */ + record DriveMotor(List motors) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.DRIVE_MOTOR; } - /** - * 单个驱动电机数据。 - * - *

2016 版字段大小(表 B.16):转速 2 字节偏移 20000,转矩 2 字节偏移 20000 单位 0.1 N·m,共 12 字节。 - *

2025 版字段大小(表 16):转速 2 字节偏移 32000,转矩 4 字节偏移 20000 N·m 单位 0.1 N·m,共 14 字节。 - * - * @param serialNo 电机顺序号 1~253 - * @param state 电机状态:0x01 耗电 / 0x02 发电 / 0x03 关闭 / 0x04 准备 - * @param controllerTempC 电机控制器温度 ℃,-40~+210 - * @param rpm 电机转速 r/min - * @param torqueNm 电机转矩 N·m,最小计量 0.1 N·m - * @param motorTempC 电机温度 ℃,-40~+210 - * @param controllerInputVoltageV 电机控制器输入电压 V,0~6000,最小计量 0.1 V - * @param controllerDcCurrentA 电机控制器直流母线电流 A,-1000~+5000,最小计量 0.1 A - */ - public record Motor( - Integer serialNo, - Integer state, - Integer controllerTempC, - Integer rpm, - Double torqueNm, - Integer motorTempC, - Double controllerInputVoltageV, - Double controllerDcCurrentA - ) {} - } + public record Motor( + Integer serialNo, + Integer state, + Integer controllerTempC, + Integer rpm, + Double torqueNm, + Integer motorTempC, + Double controllerInputVoltageV, + Double controllerDcCurrentA + ) {} + } - // ======================================================================== - // 0x03 燃料电池数据(2016 版) - // ======================================================================== + /** 0x03 燃料电池数据(2016 版字段顺序,含 N×探针温度 1B)。变长。 */ + record FuelCell( + Double fcVoltageV, + Double fcCurrentA, + Double hydrogenConsumptionKgPer100km, + List probeTempC, + Double maxHydrogenSystemTempC, + Integer maxHydrogenSystemTempProbeId, + Double maxHydrogenConcentrationPercent, + Integer maxHydrogenConcentrationProbeId, + Double maxHydrogenPressureMpa, + Integer maxHydrogenPressureProbeId, + Integer hvDcDcStatus + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.FUEL_CELL_V2016; } + } - /** - * 燃料电池数据(2016 版)。变长。 - * - *

2016 版字段顺序:燃料电池电压(2) + 电流(2) + 氢气消耗率(2) + 探针数(2) - * + N×探针温度(1) + 最高温度(2) + 最高温度探针代号(1) + 最高浓度(2) + 最高浓度代号(1) - * + 最高压力(2) + 最高压力代号(1) + DCDC 状态(1)。 - */ - record FuelCellV2016( - Double fcVoltageV, - Double fcCurrentA, - Double hydrogenConsumptionKgPer100km, - List probeTempC, - Double maxHydrogenSystemTempC, - Integer maxHydrogenSystemTempProbeId, - Double maxHydrogenConcentrationPercent, - Integer maxHydrogenConcentrationProbeId, - Double maxHydrogenPressureMpa, - Integer maxHydrogenPressureProbeId, - Integer hvDcDcStatus - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.FUEL_CELL_V2016; } - } + /** 0x04 发动机数据。固定 5 字节。 */ + record Engine( + Integer engineState, + Integer crankshaftRpm, + Double fuelConsumptionRate + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.ENGINE; } + } - // ======================================================================== - // 0x03 燃料电池发动机及车载氢系统数据(2025 版,表 17) - // ======================================================================== + /** 0x05 车辆位置数据。固定 9 字节。 */ + record Position( + int statusFlag, + double longitude, + double latitude + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.POSITION_V2016; } + } - /** - * 燃料电池发动机及车载氢系统数据(2025 版)。固定 13 字节。 - * - *

较 2016 版的差异(见 2025 规范前言 o): - *

    - *
  • 删除:燃料电池电压、燃料电池电流、氢气消耗率、温度探针列表 - *
  • 新增:剩余氢量百分比、高压 DC/DC 控制器温度 - *
  • 温度探针数据迁移至 0x30 燃料电池电堆 - *
- */ - record FuelCellV2025( - Double maxHydrogenSystemTempC, - Integer maxHydrogenSystemTempProbeId, - Double maxHydrogenConcentrationPercent, - Integer maxHydrogenConcentrationProbeId, - Double maxHydrogenPressureMpa, - Integer maxHydrogenPressureProbeId, - Integer hvDcDcStatus, - Integer remainingHydrogenPercent, - Integer hvDcDcControllerTempC - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.FUEL_CELL_V2025; } - } + /** 0x06 动力蓄电池极值数据。固定 14 字节。 */ + record Extreme( + Integer maxCellVoltageSubSysNo, + Integer maxCellVoltageCellId, + Double maxCellVoltageV, + Integer minCellVoltageSubSysNo, + Integer minCellVoltageCellId, + Double minCellVoltageV, + Integer maxTempSubSysNo, + Integer maxTempProbeId, + Integer maxTempC, + Integer minTempSubSysNo, + Integer minTempProbeId, + Integer minTempC + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.EXTREME_V2016; } + } - // ======================================================================== - // 0x04 发动机数据 —— 表 20 - // ======================================================================== + /** 0x07 报警数据。变长(4 个故障代码列表)。 */ + record Alarm( + Integer maxLevel, + long generalAlarmFlag, + List batteryFaults, + List motorFaults, + List engineFaults, + List otherFaults + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.ALARM_V2016; } + } - /** - * 发动机数据。固定 5 字节。 - * - * @param engineState 发动机状态:0x01 启动 / 0x02 关闭 - * @param crankshaftRpm 曲轴转速 r/min,0~60000 - * @param fuelConsumptionRate 燃料消耗率 L/100km,0~600,最小计量 0.01 - */ - record Engine( - Integer engineState, - Integer crankshaftRpm, - Double fuelConsumptionRate - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.ENGINE; } - } + /** 0x08 可充电储能装置电压数据。变长。PoC 仅保留汇总。 */ + record Voltage( + int subSystemCount, + double maxCellVoltageV, + double minCellVoltageV, + double totalVoltageV + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.VOLTAGE_V2016; } + } - // ======================================================================== - // 0x05 车辆位置数据 —— 表 21 - // ======================================================================== - - /** - * 车辆位置数据。 - * - *

2016 版固定 9 字节:{@code 状态位(1) + 经度(4) + 纬度(4)}。 - *

2025 版固定 10 字节:{@code 状态位(1) + 坐标系(1) + 经度(4) + 纬度(4)}。 - * - * @param statusFlag 状态位:bit0 0=有效/1=无效;bit1 0=北纬/1=南纬;bit2 0=东经/1=西经 - * @param coordSystem 坐标系(2025 新增):0x01 WGS84 / 0x02 GCJ-02 / 0x03 其他;2016 版为 null - * @param longitude 经度(十进制度) - * @param latitude 纬度(十进制度) - */ - record Position( - int statusFlag, - Integer coordSystem, - double longitude, - double latitude - ) implements InfoBlock { - @Override public InfoBlockType type() { - return coordSystem == null ? InfoBlockType.POSITION_V2016 : InfoBlockType.POSITION_V2025; + /** 0x09 可充电储能装置温度数据。变长。 */ + record Temperature( + int subSystemCount, + int maxTempC, + int minTempC + ) implements Gb32960V2016 { + @Override public InfoBlockType type() { return InfoBlockType.TEMPERATURE_V2016; } } } // ======================================================================== - // 0x06 动力蓄电池极值数据 —— 仅 2016 版 + // GB/T 32960.3-2025 // ======================================================================== - /** 动力蓄电池极值数据(2016 版)。固定 14 字节。2025 版已删除。 */ - record Extreme( - Integer maxCellVoltageSubSysNo, - Integer maxCellVoltageCellId, - Double maxCellVoltageV, - Integer minCellVoltageSubSysNo, - Integer minCellVoltageCellId, - Double minCellVoltageV, - Integer maxTempSubSysNo, - Integer maxTempProbeId, - Integer maxTempC, - Integer minTempSubSysNo, - Integer minTempProbeId, - Integer minTempC - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.EXTREME_V2016; } - } + /** GB/T 32960.3-2025 §7.2.4 定义的全部信息体类型。 */ + sealed interface Gb32960V2025 extends InfoBlock + permits Gb32960V2025.Vehicle, + Gb32960V2025.DriveMotor, + Gb32960V2025.FuelCell, + Gb32960V2025.Engine, + Gb32960V2025.Position, + Gb32960V2025.Alarm, + Gb32960V2025.MinParallelVoltage, + Gb32960V2025.BatteryTemperature, + Gb32960V2025.FuelCellStack, + Gb32960V2025.SuperCapacitor, + Gb32960V2025.SuperCapacitorExtreme { - // ======================================================================== - // 报警数据 —— 2016 的 0x07 / 2025 的 0x06(表 23) - // ======================================================================== - - /** - * 报警数据。 - * - *

2016 版 typeCode=0x07,2025 版 typeCode=0x06。前半部分(maxLevel + generalAlarmFlag - * + 4 类故障代码列表)一致,2025 版在尾部额外增加"通用报警故障等级列表"。 - * - * @param protocolVersion 协议版本,决定 typeCode 映射 - * @param maxLevel 最高报警等级 0~4(0 无故障;4 热事件为最高级) - * @param generalAlarmFlag 通用报警标志(表 24 的 28 个位) - * @param batteryFaults 可充电储能装置故障代码列表 - * @param motorFaults 驱动电机故障代码列表 - * @param engineFaults 发动机故障代码列表 - * @param otherFaults 其他故障代码列表 - * @param generalAlarmLevels 2025 新增:{bit, level} 列表;2016 版为 null 或空列表 - */ - record Alarm( - ProtocolVersion protocolVersion, - Integer maxLevel, - long generalAlarmFlag, - List batteryFaults, - List motorFaults, - List engineFaults, - List otherFaults, - List generalAlarmLevels - ) implements InfoBlock { - @Override public InfoBlockType type() { - return protocolVersion == ProtocolVersion.V2025 - ? InfoBlockType.ALARM_V2025 : InfoBlockType.ALARM_V2016; + /** 0x01 整车数据。固定 18 字节(删除加速/制动踏板)。 */ + record Vehicle( + Integer vehicleState, + Integer chargingState, + Integer runningMode, + Double speedKmh, + Double totalMileageKm, + Double totalVoltageV, + Double totalCurrentA, + Integer socPercent, + Integer dcDcStatus, + Integer gearRaw, + Integer insulationResistanceKohm + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.VEHICLE; } } - /** 2025 版通用报警故障等级列表项(2 字节):bit 对应表 24 位序号,level 为故障等级。 */ - public record GeneralAlarmEntry(int bit, int level) {} + /** 0x02 驱动电机数据。变长。每电机 14 字节(rpm 偏移 32000,torque 4 字节偏移 20000)。 */ + record DriveMotor(List motors) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.DRIVE_MOTOR; } + + public record Motor( + Integer serialNo, + Integer state, + Integer controllerTempC, + Integer rpm, + Double torqueNm, + Integer motorTempC, + Double controllerInputVoltageV, + Double controllerDcCurrentA + ) {} + } + + /** 0x03 燃料电池发动机及车载氢系统数据。固定 13 字节。 */ + record FuelCell( + Double maxHydrogenSystemTempC, + Integer maxHydrogenSystemTempProbeId, + Double maxHydrogenConcentrationPercent, + Integer maxHydrogenConcentrationProbeId, + Double maxHydrogenPressureMpa, + Integer maxHydrogenPressureProbeId, + Integer hvDcDcStatus, + Integer remainingHydrogenPercent, + Integer hvDcDcControllerTempC + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.FUEL_CELL_V2025; } + } + + /** 0x04 发动机数据。固定 5 字节。字段同 2016 版。 */ + record Engine( + Integer engineState, + Integer crankshaftRpm, + Double fuelConsumptionRate + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.ENGINE; } + } + + /** 0x05 车辆位置数据。固定 10 字节(多坐标系字节)。 */ + record Position( + int statusFlag, + int coordSystem, + double longitude, + double latitude + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.POSITION_V2025; } + } + + /** 0x06 报警数据。变长(4 个故障代码列表 + 通用报警故障等级列表)。 */ + record Alarm( + Integer maxLevel, + long generalAlarmFlag, + List batteryFaults, + List motorFaults, + List engineFaults, + List otherFaults, + List generalAlarmLevels + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.ALARM_V2025; } + + /** 通用报警故障等级列表项(2 字节):bit 对应表 24 位序号,level 为故障等级。 */ + public record GeneralAlarmEntry(int bit, int level) {} + } + + /** 0x07 动力蓄电池最小并联单元电压数据。变长。 */ + record MinParallelVoltage(int subSystemCount, List packs) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.MIN_PARALLEL_VOLTAGE_V2025; } + + public record Pack( + int packNo, + Double packVoltageV, + Double packCurrentA, + int totalUnits, + List frameVoltages + ) {} + } + + /** 0x08 动力蓄电池温度数据。变长。 */ + record BatteryTemperature(int subSystemCount, List packs) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.BATTERY_TEMPERATURE_V2025; } + + public record Pack(int packNo, int probeCount, List probeTempsC) {} + } + + /** 0x30 燃料电池电堆数据。变长(2025 标准)。 */ + record FuelCellStack(int stackCount, List stacks) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.FUEL_CELL_STACK; } + + public record Stack( + int stackNo, + Double voltageV, + Double currentA, + Double hydrogenInletPressureKpa, + Double airInletPressureKpa, + Integer airInletTempC, + List coolantOutletProbeTempsC + ) {} + } + + /** 0x31 超级电容器数据。变长。 */ + record SuperCapacitor( + int systemNo, + Double totalVoltageV, + Double totalCurrentA, + List cellVoltages, + List probeTempsC + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.SUPER_CAPACITOR; } + } + + /** 0x32 超级电容器极值数据。固定 18 字节。 */ + record SuperCapacitorExtreme( + Integer maxVoltageSystemNo, + Integer maxVoltageCellId, + Double maxCellVoltageV, + Integer minVoltageSystemNo, + Integer minVoltageCellId, + Double minCellVoltageV, + Integer maxTempSystemNo, + Integer maxTempProbeId, + Integer maxTempC, + Integer minTempSystemNo, + Integer minTempProbeId, + Integer minTempC + ) implements Gb32960V2025 { + @Override public InfoBlockType type() { return InfoBlockType.SUPER_CAPACITOR_EXTREME; } + } } // ======================================================================== - // 2016 版 0x08 可充电储能装置电压数据 + // 广东燃料电池汽车示范应用城市群综合监管平台规范 v1.0(基于 GB/T 32960.3-2016 帧头) // ======================================================================== - /** 可充电储能装置电压数据(2016 版)。变长。PoC 仅保留汇总。 */ - record Voltage( - int subSystemCount, - double maxCellVoltageV, - double minCellVoltageV, - double totalVoltageV - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.VOLTAGE_V2016; } - } + /** 广东燃料电池汽车示范应用规范定义的扩展信息体(typeCode 0x30~0x34、0x80)。 */ + sealed interface GuangdongFc extends InfoBlock + permits GuangdongFc.Stack, + GuangdongFc.Auxiliary, + GuangdongFc.DcDc, + GuangdongFc.AirConditioner, + GuangdongFc.VehicleInfo, + GuangdongFc.DemoExtension { - // ======================================================================== - // 2016 版 0x09 可充电储能装置温度数据 - // ======================================================================== + /** 0x30 燃料电池电堆数据(§7.2.3.4 表 13/14)。变长。 */ + record Stack(int stackCount, List stacks) implements GuangdongFc { + @Override public InfoBlockType type() { return InfoBlockType.GD_FC_STACK; } - /** 可充电储能装置温度数据(2016 版)。变长。 */ - record Temperature( - int subSystemCount, - int maxTempC, - int minTempC - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.TEMPERATURE_V2016; } - } + public record Item( + Integer engineWorkState, + Integer stackWaterOutletTempC, + Double hydrogenInletPressureKpa, + Double airInletPressureKpa, + Integer airInletTempC, + Integer maxCellVoltageId, + Integer minCellVoltageId, + Double maxCellVoltageV, + Double minCellVoltageV, + Double avgCellVoltageV, + Integer cellCount, + Integer frameCellStart, + Integer frameCellCount, + List frameCellVoltagesV + ) {} + } - // ======================================================================== - // 2025 版 0x07 动力蓄电池最小并联单元电压数据(表 11/12) - // ======================================================================== + /** 0x31 辅助系统数据(§7.2.3.5 表 15/16)。变长。 */ + record Auxiliary(int subSystemCount, List subsystems) implements GuangdongFc { + @Override public InfoBlockType type() { return InfoBlockType.GD_FC_AUXILIARY; } - /** 动力蓄电池最小并联单元电压数据(2025 版)。变长。 */ - record MinParallelVoltage(int subSystemCount, List packs) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.MIN_PARALLEL_VOLTAGE_V2025; } + public record Subsystem( + Double airCompressorMotorVoltageV, + Integer airCompressorPowerKw, + Double hydrogenPumpMotorVoltageV, + Integer hydrogenPumpPowerKw, + Double waterPumpVoltageV, + Double ptcVoltageV, + Integer ptcPowerKw, + Double lowVoltageBatteryVoltageV + ) {} + } - /** - * @param packNo 电池包号 1~50 - * @param packVoltageV 动力蓄电池包电压 V,0~6000,最小 0.1 V - * @param packCurrentA 动力蓄电池包电流 A,-3000~+3000,最小 0.1 A - * @param totalUnits 最小并联单元总数 1~65531 - * @param frameVoltages 本帧最小并联单元电压列表,0~60 V,最小 0.001 V - */ - public record Pack( - int packNo, - Double packVoltageV, - Double packCurrentA, - int totalUnits, - List frameVoltages - ) {} - } + /** 0x32 DC/DC(燃料电池系统)数据(§7.2.3.6 表 17)。固定 9 字节。 */ + record DcDc( + Double inputVoltageV, + Double inputCurrentA, + Double outputVoltageV, + Double outputCurrentA, + Integer controllerTempC + ) implements GuangdongFc { + @Override public InfoBlockType type() { return InfoBlockType.GD_FC_DCDC; } + } - // ======================================================================== - // 2025 版 0x08 动力蓄电池温度数据(表 13/14) - // ======================================================================== + /** 0x33 空调数据(§7.2.3.7 表 18)。固定 5 字节。 */ + record AirConditioner( + Integer status, + Integer powerKw, + Double compressorInputVoltageV + ) implements GuangdongFc { + @Override public InfoBlockType type() { return InfoBlockType.GD_FC_AIR_CONDITIONER; } + } - /** 动力蓄电池温度数据(2025 版)。变长。 */ - record BatteryTemperature(int subSystemCount, List packs) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.BATTERY_TEMPERATURE_V2025; } + /** 0x34 车辆信息数据(§7.2.3.8 表 19)。固定 7 字节。 */ + record VehicleInfo( + Integer collisionAlarm, + Integer ambientTempC, + Double ambientPressureKpa, + Double hydrogenMassKg + ) implements GuangdongFc { + @Override public InfoBlockType type() { return InfoBlockType.GD_FC_VEHICLE_INFO; } + } - /** - * @param packNo 电池包号 1~50 - * @param probeCount 温度探针个数 1~65531 - * @param probeTempsC 各探针温度 ℃,-40~+210 - */ - public record Pack(int packNo, int probeCount, List probeTempsC) {} - } - - // ======================================================================== - // 2025 版 0x30 燃料电池电堆数据(表 18/19) - // ======================================================================== - - /** 燃料电池电堆数据(2025 版)。变长:1 字节电堆个数 + N × 电堆信息。 */ - record FuelCellStack(int stackCount, List stacks) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.FUEL_CELL_STACK; } - - /** - * @param stackNo 电堆数据序号 1~253 - * @param voltageV 燃料电池电堆电压 V,0~2000,最小 0.1 V - * @param currentA 燃料电池电堆电流 A,0~2000,最小 0.1 A - * @param hydrogenInletPressureKpa 氢气入口压力 kPa,-100~+400(偏移 100 kPa),最小 0.1 kPa - * @param airInletPressureKpa 空气入口压力 kPa,-100~+400,最小 0.1 kPa - * @param airInletTempC 空气入口温度 ℃,-40~+210 - * @param coolantOutletProbeTempsC 冷却水出水口温度探针温度列表 ℃ - */ - public record Stack( - int stackNo, - Double voltageV, - Double currentA, - Double hydrogenInletPressureKpa, - Double airInletPressureKpa, - Integer airInletTempC, - List coolantOutletProbeTempsC - ) {} - } - - // ======================================================================== - // 2025 版 0x31 超级电容器数据(表 25) - // ======================================================================== - - /** 超级电容器数据(2025 版)。变长。 */ - record SuperCapacitor( - int systemNo, - Double totalVoltageV, - Double totalCurrentA, - List cellVoltages, - List probeTempsC - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.SUPER_CAPACITOR; } - } - - // ======================================================================== - // 2025 版 0x32 超级电容器极值数据(表 26) - // ======================================================================== - - /** 超级电容器极值数据(2025 版)。固定 14 字节。 */ - record SuperCapacitorExtreme( - Integer maxVoltageSystemNo, - Integer maxVoltageCellId, - Double maxCellVoltageV, - Integer minVoltageSystemNo, - Integer minVoltageCellId, - Double minCellVoltageV, - Integer maxTempSystemNo, - Integer maxTempProbeId, - Integer maxTempC, - Integer minTempSystemNo, - Integer minTempProbeId, - Integer minTempC - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.SUPER_CAPACITOR_EXTREME; } - } - - // ======================================================================== - // 广东燃料电池汽车示范规范扩展块(typeCode 0x30~0x34、0x80) - // ======================================================================== - - /** - * 广东规范 0x30 燃料电池电堆数据(表 13/14)。变长。 - * 一个 stackCount + 多个 stack;每个 stack 包含定长字段后跟 frameCellCount 个单体电压(2B/个)。 - */ - record GdFcStack(int stackCount, List stacks) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.GD_FC_STACK; } - - public record Stack( - Integer engineWorkState, // 1B - Integer stackWaterOutletTempC, // 1B (-40 offset) - Double hydrogenInletPressureKpa, // 2B (-100 offset, 0.1 kPa) - Double airInletPressureKpa, // 2B (-100 offset, 0.1 kPa) - Integer airInletTempC, // 1B (-40 offset) - Integer maxCellVoltageId, // 2B - Integer minCellVoltageId, // 2B - Double maxCellVoltageV, // 2B (0.001 V) - Double minCellVoltageV, // 2B (0.001 V) - Double avgCellVoltageV, // 2B (0.001 V) - Integer cellCount, // 2B - Integer frameCellStart, // 2B - Integer frameCellCount, // 1B - List frameCellVoltagesV // 2*m B (0.001 V) - ) {} - } - - /** 广东规范 0x31 辅助系统数据(表 15/16)。变长。 */ - record GdFcAuxiliary(int subSystemCount, List subsystems) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.GD_FC_AUXILIARY; } - - public record Subsystem( - Double airCompressorMotorVoltageV, // 2B 0.1V - Integer airCompressorPowerKw, // 2B (-5 offset, 1 kw) - Double hydrogenPumpMotorVoltageV, // 2B 0.2V - Integer hydrogenPumpPowerKw, // 2B (-5 offset, 1 kw) - Double waterPumpVoltageV, // 2B 0.1V - Double ptcVoltageV, // 2B 0.1V - Integer ptcPowerKw, // 2B (-5 offset, 1 kw) - Double lowVoltageBatteryVoltageV // 2B 0.1V - ) {} - } - - /** 广东规范 0x32 DC/DC(燃料电池系统)数据(表 17)。固定 9 字节。 */ - record GdFcDcDc( - Double inputVoltageV, // 2B 0.1V (0~400V) - Double inputCurrentA, // 2B 0.1A (0~600A) - Double outputVoltageV, // 2B 0.1V (0~700V) - Double outputCurrentA, // 2B 0.1A (0~600A) - Integer controllerTempC // 1B -40 offset - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.GD_FC_DCDC; } - } - - /** 广东规范 0x33 空调数据(表 18)。固定 5 字节。 */ - record GdFcAirConditioner( - Integer status, // 1B 0 关闭 / 1 启动 - Integer powerKw, // 2B (-5 offset, 1 kw, range -5~+30) - Double compressorInputVoltageV // 2B 0.1V (0~1000V) - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.GD_FC_AIR_CONDITIONER; } - } - - /** 广东规范 0x34 车辆信息数据(表 19)。固定 7 字节。 */ - record GdFcVehicleInfo( - Integer collisionAlarm, // 1B 0 无 / 1 有 - Integer ambientTempC, // 2B (-40 offset, 1°C) - Double ambientPressureKpa, // 2B (-100 offset, 0.1 kPa) - Double hydrogenMassKg // 2B 0.1 kg - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.GD_FC_VEHICLE_INFO; } - } - - /** - * 广东规范 0x80 燃料电池汽车示范应用数据扩展(表 27)。固定 12 字节 - * (含 2 字节内层 length=9 + 9 字节数据)。 - */ - record GdFcDemoExtension( - Integer stackTempC, // 1B -40 offset - Double airCompressorVoltageV, // 2B 0.1V (0~2000V) - Double airCompressorCurrentA, // 2B 0.1A (-1000 offset) - Double hydrogenPumpVoltageV, // 2B 0.1V (0~2000V) - Double hydrogenPumpCurrentA // 2B 0.1A (-1000 offset) - ) implements InfoBlock { - @Override public InfoBlockType type() { return InfoBlockType.GD_FC_DEMO_EXTENSION; } + /** 0x80 燃料电池汽车示范应用数据扩展(§7.2.3.14 表 27)。 */ + record DemoExtension( + Integer stackTempC, + Double airCompressorVoltageV, + Double airCompressorCurrentA, + Double hydrogenPumpVoltageV, + Double hydrogenPumpCurrentA + ) implements GuangdongFc { + @Override public InfoBlockType type() { return InfoBlockType.GD_FC_DEMO_EXTENSION; } + } } // ======================================================================== diff --git a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderGoldenTest.java b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderGoldenTest.java index fcbe1a52..91b92f66 100644 --- a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderGoldenTest.java +++ b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderGoldenTest.java @@ -78,7 +78,7 @@ class Gb32960DecoderGoldenTest { .isPresent(); } // 总电流必须落在协议规定的 -1000~+1000 A 区间,防回归到 -3000 偏移 - msg.findBlock(InfoBlock.Vehicle.class).ifPresent(v -> { + msg.findBlock(InfoBlock.Gb32960V2016.Vehicle.class).ifPresent(v -> { if (v.totalCurrentA() != null) { assertThat(v.totalCurrentA()) .as("[%s] vehicle.totalCurrentA 越界,疑似偏移常量回归", name) diff --git a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderTest.java b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderTest.java index ca027018..da45e891 100644 --- a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderTest.java +++ b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960DecoderTest.java @@ -37,12 +37,12 @@ public class Gb32960DecoderTest { assertThat(msg.header().vin()).isEqualTo("LTEST000000000001"); assertThat(msg.header().eventTime()).isNotNull(); - InfoBlock.Vehicle v = msg.findBlock(InfoBlock.Vehicle.class).orElseThrow(); + InfoBlock.Gb32960V2016.Vehicle v = msg.findBlock(InfoBlock.Gb32960V2016.Vehicle.class).orElseThrow(); assertThat(v.socPercent()).isEqualTo(70); assertThat(v.speedKmh()).isEqualTo(52.3, org.assertj.core.data.Offset.offset(0.01)); assertThat(v.gearRaw()).isEqualTo(0x0F); - InfoBlock.Position p = msg.findBlock(InfoBlock.Position.class).orElseThrow(); + InfoBlock.Gb32960V2016.Position p = msg.findBlock(InfoBlock.Gb32960V2016.Position.class).orElseThrow(); assertThat(p.longitude()).isEqualTo(116.397128, org.assertj.core.data.Offset.offset(0.000001)); assertThat(p.latitude()).isEqualTo(39.916527, org.assertj.core.data.Offset.offset(0.000001)); } diff --git a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960FullBlocksTest.java b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960FullBlocksTest.java index 03e40c7c..1620957b 100644 --- a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960FullBlocksTest.java +++ b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960FullBlocksTest.java @@ -48,7 +48,7 @@ class Gb32960FullBlocksTest { writeU16(os, 11000); // current raw → 100 A }); Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame)); - InfoBlock.DriveMotor dm = msg.findBlock(InfoBlock.DriveMotor.class).orElseThrow(); + InfoBlock.Gb32960V2016.DriveMotor dm = msg.findBlock(InfoBlock.Gb32960V2016.DriveMotor.class).orElseThrow(); assertThat(dm.motors()).hasSize(1); var m = dm.motors().get(0); assertThat(m.rpm()).isEqualTo(3000); @@ -68,7 +68,7 @@ class Gb32960FullBlocksTest { os.write(1); writeU32(os, 0xCAFE_BABEL); // other faults 1 }); Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame)); - InfoBlock.Alarm a = msg.findBlock(InfoBlock.Alarm.class).orElseThrow(); + InfoBlock.Gb32960V2016.Alarm a = msg.findBlock(InfoBlock.Gb32960V2016.Alarm.class).orElseThrow(); assertThat(a.maxLevel()).isEqualTo(2); assertThat(a.batteryFaults()).hasSize(1); assertThat(a.otherFaults()).hasSize(1); @@ -90,7 +90,7 @@ class Gb32960FullBlocksTest { writeU16(os, 3300); // 3.3V }); Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame)); - InfoBlock.Voltage v = msg.findBlock(InfoBlock.Voltage.class).orElseThrow(); + InfoBlock.Gb32960V2016.Voltage v = msg.findBlock(InfoBlock.Gb32960V2016.Voltage.class).orElseThrow(); assertThat(v.subSystemCount()).isEqualTo(1); assertThat(v.maxCellVoltageV()).isEqualTo(3.6, org.assertj.core.data.Offset.offset(0.001)); assertThat(v.minCellVoltageV()).isEqualTo(3.3, org.assertj.core.data.Offset.offset(0.001)); @@ -109,7 +109,7 @@ class Gb32960FullBlocksTest { os.write(55); // actual 15 }); Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame)); - InfoBlock.Temperature t = msg.findBlock(InfoBlock.Temperature.class).orElseThrow(); + InfoBlock.Gb32960V2016.Temperature t = msg.findBlock(InfoBlock.Gb32960V2016.Temperature.class).orElseThrow(); assertThat(t.maxTempC()).isEqualTo(30); assertThat(t.minTempC()).isEqualTo(15); } diff --git a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcParserTest.java b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcParserTest.java index f9d65acf..0aedda7f 100644 --- a/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcParserTest.java +++ b/protocol-gb32960/src/test/java/com/lingniu/ingest/protocol/gb32960/codec/parser/vendor/guangdong/GdFcParserTest.java @@ -24,7 +24,8 @@ class GdFcParserTest { 0x04, 0x52, // 0x0452 = 1106 → 110.6 A 0x69 // 0x69 = 105 - 40 = 65 °C }); - InfoBlock.GdFcDcDc b = (InfoBlock.GdFcDcDc) new GdFcDcDcBlockParser().parse(buf); + InfoBlock.GuangdongFc.DcDc b = + (InfoBlock.GuangdongFc.DcDc) new GdFcDcDcBlockParser().parse(buf); assertThat(b.inputVoltageV()).isCloseTo(300.0, within(1e-6)); assertThat(b.inputCurrentA()).isCloseTo(120.0, within(1e-6)); assertThat(b.outputVoltageV()).isCloseTo(560.0, within(1e-6)); @@ -40,8 +41,8 @@ class GdFcParserTest { 0x00, 0x14, // 20 - 5 = 15 kw 0x15, (byte) 0x18 // 0x1518 = 5400 → 540.0 V }); - InfoBlock.GdFcAirConditioner b = - (InfoBlock.GdFcAirConditioner) new GdFcAirConditionerBlockParser().parse(buf); + InfoBlock.GuangdongFc.AirConditioner b = + (InfoBlock.GuangdongFc.AirConditioner) new GdFcAirConditionerBlockParser().parse(buf); assertThat(b.status()).isEqualTo(1); assertThat(b.powerKw()).isEqualTo(15); assertThat(b.compressorInputVoltageV()).isCloseTo(540.0, within(1e-6)); @@ -56,8 +57,8 @@ class GdFcParserTest { 0x04, 0x4C, // 0x044C = 1100 → 110*0.1 - 100 = 10.0 kPa 0x00, 0x50 // 0x0050 = 80 → 8.0 kg }); - InfoBlock.GdFcVehicleInfo b = - (InfoBlock.GdFcVehicleInfo) new GdFcVehicleInfoBlockParser().parse(buf); + InfoBlock.GuangdongFc.VehicleInfo b = + (InfoBlock.GuangdongFc.VehicleInfo) new GdFcVehicleInfoBlockParser().parse(buf); assertThat(b.collisionAlarm()).isEqualTo(0); assertThat(b.ambientTempC()).isEqualTo(30); assertThat(b.ambientPressureKpa()).isCloseTo(10.0, within(1e-6)); @@ -75,8 +76,8 @@ class GdFcParserTest { 0x00, 0x0D, // 0x000D = 13 → 1.3V 0x27, 0x10 // 0 A }); - InfoBlock.GdFcDemoExtension b = - (InfoBlock.GdFcDemoExtension) new GdFcDemoExtensionBlockParser().parse(buf); + InfoBlock.GuangdongFc.DemoExtension b = + (InfoBlock.GuangdongFc.DemoExtension) new GdFcDemoExtensionBlockParser().parse(buf); assertThat(b.stackTempC()).isEqualTo(50); assertThat(b.airCompressorVoltageV()).isCloseTo(372.0, within(1e-6)); assertThat(b.airCompressorCurrentA()).isCloseTo(0.0, within(1e-6));