chore: initial import of lingniu-vehicle-ingest

Multi-module Spring Boot ingest service for vehicle telemetry. Modules:

- ingest-api / ingest-core / ingest-codec-common: shared SPI, dispatcher,
  Disruptor event bus, BCC/BCD codec helpers
- protocol-gb32960: GB/T 32960.3 inbound (Netty + per-version parser
  packages v2016/v2025), platform login auth, VIN whitelist, idle handler
- protocol-jt808 / protocol-jt1078 / protocol-jsatl12: JT/T inbound
- inbound-mqtt / inbound-xinda-push: alternative ingest channels
- session-core: per-channel session state
- sink-archive / sink-mq: persistence sinks (local file / Kafka)
- command-gateway: terminal control command gateway
- bootstrap-all: aggregator Spring Boot app
- observability: Micrometer / Actuator wiring

Includes hex-dump golden samples under protocol-gb32960/src/test/resources
and the GB/T 32960.3-2016 / 2025 reference PDFs under reference/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lingniu-dev
2026-04-15 16:08:57 +08:00
commit 064ecc479c
220 changed files with 11874 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
package com.lingniu.ingest.protocol.gb32960.codec;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.AlarmV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.DriveMotorV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.EngineV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.ExtremeValueV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.FuelCellV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.PositionV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.TemperatureV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VehicleV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VoltageV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.model.Gb32960Message;
import com.lingniu.ingest.protocol.gb32960.model.InfoBlock;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 黄金样本集回放测试:遍历 {@code src/test/resources/samples/gb32960/*.hex}
* 逐帧解码并断言命令类型来自 {@link com.lingniu.ingest.protocol.gb32960.model.CommandType} 有效值。
*
* <p>样本文件目前为空,该测试会优雅地跳过。加入样本后会自动变为 N 条动态用例。
*/
class Gb32960DecoderGoldenTest {
private final Gb32960MessageDecoder decoder = new Gb32960MessageDecoder(
new Gb32960BodyParser(new InfoBlockParserRegistry(List.of(
new VehicleV2016BlockParser(),
new PositionV2016BlockParser(),
new DriveMotorV2016BlockParser(),
new FuelCellV2016BlockParser(),
new EngineV2016BlockParser(),
new ExtremeValueV2016BlockParser(),
new AlarmV2016BlockParser(),
new VoltageV2016BlockParser(),
new TemperatureV2016BlockParser()))));
@TestFactory
Collection<DynamicTest> replaySamples() throws URISyntaxException, IOException {
var url = getClass().getClassLoader().getResource("samples/gb32960");
if (url == null) return List.of();
Path dir = Paths.get(url.toURI());
try (Stream<Path> s = Files.list(dir)) {
return s.filter(p -> p.toString().endsWith(".hex"))
.sorted()
.map(this::toTest)
.collect(Collectors.toList());
}
}
private DynamicTest toTest(Path sample) {
return DynamicTest.dynamicTest(sample.getFileName().toString(), () -> {
byte[] frame = readHex(sample);
Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame));
assertThat(msg.header().vin()).hasSize(17);
assertThat(msg.header().command()).isNotNull();
// 749 字节真实生产帧realtime_002/003/010/200peer 在 V2016 帧里下发了
// 0x30/0x31/0x32 等 typeCode但这些在 GB/T 32960.3-2016 附录 B 表 B.3 是"预留"区,
// 没有任何字段定义。因此**期望产生 Raw 兜底块**——这是符合规范的正确行为。
// 若未来对端切换到 V2025 (2424 起始) 或者迁移到 0x80~0xFE 用户自定义区,可再调整断言。
String name = sample.getFileName().toString();
boolean isRealProductionFrame = frame.length == 774;
if (isRealProductionFrame) {
assertThat(msg.findBlock(InfoBlock.Raw.class))
.as("[%s] 749字节真实生产帧应有 Raw 兜底块peer 越界使用 0x30+ 预留 typeCode", name)
.isPresent();
}
// 总电流必须落在协议规定的 -1000~+1000 A 区间,防回归到 -3000 偏移
msg.findBlock(InfoBlock.Vehicle.class).ifPresent(v -> {
if (v.totalCurrentA() != null) {
assertThat(v.totalCurrentA())
.as("[%s] vehicle.totalCurrentA 越界,疑似偏移常量回归", name)
.isBetween(-1000.0, 1000.0);
}
});
});
}
private static byte[] readHex(Path path) throws IOException {
StringBuilder sb = new StringBuilder();
for (String line : Files.readAllLines(path)) {
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) continue;
sb.append(trimmed);
}
String hex = sb.toString();
int len = hex.length() / 2;
byte[] out = new byte[len];
for (int i = 0; i < len; i++) {
out[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return out;
}
}

View File

@@ -0,0 +1,110 @@
package com.lingniu.ingest.protocol.gb32960.codec;
import com.lingniu.ingest.codec.BccChecksum;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.PositionV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VehicleV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.model.CommandType;
import com.lingniu.ingest.protocol.gb32960.model.Gb32960Message;
import com.lingniu.ingest.protocol.gb32960.model.InfoBlock;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 构造一条合成 32960 实时上报帧,跑通 Frame 解码 → Body 解析 → InfoBlock 的全链路。
*
* <p>样本无需外部文件:本测试既验证解码正确性,也作为 {@code samples/} 黄金样本的期望值参考实现。
*/
public class Gb32960DecoderTest {
@Test
void decodesSyntheticRealtimeReport() {
byte[] frame = buildRealtimeFrame("LTEST000000000001");
InfoBlockParserRegistry registry = new InfoBlockParserRegistry(
List.of(new VehicleV2016BlockParser(), new PositionV2016BlockParser()));
Gb32960BodyParser bodyParser = new Gb32960BodyParser(registry);
Gb32960MessageDecoder decoder = new Gb32960MessageDecoder(bodyParser);
Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame));
assertThat(msg.header().command()).isEqualTo(CommandType.REALTIME_REPORT);
assertThat(msg.header().vin()).isEqualTo("LTEST000000000001");
assertThat(msg.header().eventTime()).isNotNull();
InfoBlock.Vehicle v = msg.findBlock(InfoBlock.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();
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));
}
/**
* 构造一条合法的 32960 实时上报帧:
* header + 6B 时间戳(2024-01-02 03:04:05) + 0x01 整车 + 0x05 位置 + BCC
*/
public static byte[] buildRealtimeFrame(String vin) {
ByteArrayOutputStream body = new ByteArrayOutputStream();
// 时间戳
body.write(24); body.write(1); body.write(2); body.write(3); body.write(4); body.write(5);
// 0x01 整车 20 字节
body.write(0x01);
body.write(0x01); // vehicle state = 启动
body.write(0x03); // charging = 未充电
body.write(0x01); // 纯电
writeU16(body, 523); // 车速 52.3 km/h
writeU32(body, 1234567); // 里程 123456.7 km
writeU16(body, 6000); // 600.0 V
writeU16(body, 1100); // 110.0 A (110 - (1000-1000) = 10) 实际计算1100*0.1 - 1000 = -890此处只为结构测试
body.write(70); // SOC
body.write(0x01);
body.write(0x0F);
writeU16(body, 500);
body.write(30);
body.write(0);
// 0x05 位置 9 字节
body.write(0x05);
body.write(0x00); // 有效 + 北纬 + 东经
writeU32(body, 116_397_128L);
writeU32(body, 39_916_527L);
byte[] bodyBytes = body.toByteArray();
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(0x23); out.write(0x23);
out.write(0x02); // 实时上报
out.write(0xFE); // 应答 / 命令
byte[] vinBytes = vin.getBytes(StandardCharsets.US_ASCII);
out.write(vinBytes, 0, 17);
out.write(0x01); // 不加密
writeU16(out, bodyBytes.length);
out.write(bodyBytes, 0, bodyBytes.length);
byte[] almost = out.toByteArray();
byte bcc = BccChecksum.compute(almost, 2, almost.length - 2);
out.write(bcc & 0xFF);
return out.toByteArray();
}
private static void writeU16(ByteArrayOutputStream os, int v) {
os.write((v >> 8) & 0xFF);
os.write(v & 0xFF);
}
private static void writeU32(ByteArrayOutputStream os, long v) {
os.write((int) ((v >> 24) & 0xFF));
os.write((int) ((v >> 16) & 0xFF));
os.write((int) ((v >> 8) & 0xFF));
os.write((int) (v & 0xFF));
}
}

View File

@@ -0,0 +1,152 @@
package com.lingniu.ingest.protocol.gb32960.codec;
import com.lingniu.ingest.codec.BccChecksum;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.AlarmV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.DriveMotorV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.PositionV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.TemperatureV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VehicleV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VoltageV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.model.Gb32960Message;
import com.lingniu.ingest.protocol.gb32960.model.InfoBlock;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 验证新增的变长信息体 Parser0x02 驱动电机 / 0x07 报警 / 0x08 电压 / 0x09 温度)
* 与主解码器协同工作。
*/
class Gb32960FullBlocksTest {
private final Gb32960MessageDecoder decoder = new Gb32960MessageDecoder(
new Gb32960BodyParser(new InfoBlockParserRegistry(List.of(
new VehicleV2016BlockParser(),
new PositionV2016BlockParser(),
new DriveMotorV2016BlockParser(),
new AlarmV2016BlockParser(),
new VoltageV2016BlockParser(),
new TemperatureV2016BlockParser()))));
@Test
void parsesDriveMotorBlock() {
byte[] frame = buildFrame(os -> {
os.write(0x02); // drive motor
os.write(1); // 1 motor
os.write(1); // serial
os.write(0x01); // state
os.write(100); // controllerTemp = 60
writeU16(os, 23_000); // rpm = 3000
writeU16(os, 21_000); // torque raw, actual = 100 Nm
os.write(90); // motorTemp = 50
writeU16(os, 5400); // voltage 540.0V
writeU16(os, 11000); // current raw → 100 A
});
Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame));
InfoBlock.DriveMotor dm = msg.findBlock(InfoBlock.DriveMotor.class).orElseThrow();
assertThat(dm.motors()).hasSize(1);
var m = dm.motors().get(0);
assertThat(m.rpm()).isEqualTo(3000);
assertThat(m.torqueNm()).isEqualTo(100.0);
assertThat(m.controllerInputVoltageV()).isEqualTo(540.0);
}
@Test
void parsesAlarmBlock() {
byte[] frame = buildFrame(os -> {
os.write(0x07); // alarm
os.write(2); // max level
writeU32(os, 0x0000_0003L);// general flag
os.write(1); writeU32(os, 0xDEAD_BEEFL); // battery faults 1
os.write(0); // motor faults 0
os.write(0); // engine faults 0
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();
assertThat(a.maxLevel()).isEqualTo(2);
assertThat(a.batteryFaults()).hasSize(1);
assertThat(a.otherFaults()).hasSize(1);
}
@Test
void parsesVoltageBlock() {
byte[] frame = buildFrame(os -> {
os.write(0x08); // voltage
os.write(1); // 1 subsystem
os.write(1); // battery index
writeU16(os, 3800); // 380.0 V
writeU16(os, 11_000); // current raw
writeU16(os, 96); // total cells
writeU16(os, 1); // start
os.write(3); // frame cells
writeU16(os, 3500); // 3.5V
writeU16(os, 3600); // 3.6V
writeU16(os, 3300); // 3.3V
});
Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame));
InfoBlock.Voltage v = msg.findBlock(InfoBlock.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));
}
@Test
void parsesTemperatureBlock() {
byte[] frame = buildFrame(os -> {
os.write(0x09); // temperature
os.write(1); // subsystems
os.write(1); // battery index
writeU16(os, 4); // probes
os.write(60); // actual 20
os.write(65); // actual 25
os.write(70); // actual 30
os.write(55); // actual 15
});
Gb32960Message msg = decoder.decode(ByteBuffer.wrap(frame));
InfoBlock.Temperature t = msg.findBlock(InfoBlock.Temperature.class).orElseThrow();
assertThat(t.maxTempC()).isEqualTo(30);
assertThat(t.minTempC()).isEqualTo(15);
}
// ===== frame builder helpers =====
private static byte[] buildFrame(java.util.function.Consumer<ByteArrayOutputStream> bodyWriter) {
ByteArrayOutputStream body = new ByteArrayOutputStream();
// 时间戳
body.write(24); body.write(1); body.write(2); body.write(3); body.write(4); body.write(5);
bodyWriter.accept(body);
byte[] bodyBytes = body.toByteArray();
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(0x23); out.write(0x23);
out.write(0x02); // cmd: realtime
out.write(0xFE); // response flag
byte[] vin = "LTEST000000000010".getBytes(StandardCharsets.US_ASCII);
out.write(vin, 0, 17);
out.write(0x01); // not encrypted
writeU16(out, bodyBytes.length);
out.write(bodyBytes, 0, bodyBytes.length);
byte[] almost = out.toByteArray();
byte bcc = BccChecksum.compute(almost, 2, almost.length - 2);
out.write(bcc & 0xFF);
return out.toByteArray();
}
private static void writeU16(ByteArrayOutputStream os, int v) {
os.write((v >> 8) & 0xFF);
os.write(v & 0xFF);
}
private static void writeU32(ByteArrayOutputStream os, long v) {
os.write((int) ((v >> 24) & 0xFF));
os.write((int) ((v >> 16) & 0xFF));
os.write((int) ((v >> 8) & 0xFF));
os.write((int) (v & 0xFF));
}
}

View File

@@ -0,0 +1,37 @@
package com.lingniu.ingest.protocol.gb32960.mapper;
import com.lingniu.ingest.api.ProtocolId;
import com.lingniu.ingest.api.event.VehicleEvent;
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960BodyParser;
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960DecoderTest;
import com.lingniu.ingest.protocol.gb32960.codec.Gb32960MessageDecoder;
import com.lingniu.ingest.protocol.gb32960.codec.InfoBlockParserRegistry;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.PositionV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.codec.parser.v2016.VehicleV2016BlockParser;
import com.lingniu.ingest.protocol.gb32960.model.Gb32960Message;
import org.junit.jupiter.api.Test;
import java.nio.ByteBuffer;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class Gb32960EventMapperTest {
@Test
void realtimeReportProducesRealtimeAndLocationEvents() {
byte[] frame = Gb32960DecoderTest.buildRealtimeFrame("LTEST000000000002");
Gb32960BodyParser body = new Gb32960BodyParser(new InfoBlockParserRegistry(
List.of(new VehicleV2016BlockParser(), new PositionV2016BlockParser())));
Gb32960Message msg = new Gb32960MessageDecoder(body).decode(ByteBuffer.wrap(frame));
List<VehicleEvent> events = new Gb32960EventMapper().toEvents(msg);
assertThat(events).hasSize(2);
assertThat(events).anyMatch(e -> e instanceof VehicleEvent.Realtime);
assertThat(events).anyMatch(e -> e instanceof VehicleEvent.Location);
assertThat(events).allMatch(e -> e.source() == ProtocolId.GB32960);
assertThat(events).allMatch(e -> e.vin().equals("LTEST000000000002"));
}
}

View File

@@ -0,0 +1,41 @@
# GB/T 32960 黄金样本集
> 本目录用于回放测试与双跑对账。所有样本从旧服务 `lingniu-vehicle-data-reception` 线上抓包后脱敏VIN 替换为 `LTEST<序号>XXXXXXXX`)。
## 文件命名
```
<命令类型>_<场景>_<序号>.hex
```
命令类型取自 `CommandType` 枚举:
- `vehicle_login` (0x01)
- `realtime_report` (0x02)
- `resend_report` (0x03)
- `vehicle_logout` (0x04)
- `heartbeat` (0x07)
## 文件格式
每个 `.hex` 文件是一行紧凑十六进制,字节之间**无分隔符**。
行首允许以 `#` 开头写注释,测试代码忽略注释行与空行。
示例 `realtime_report_basic_001.hex`:
```
# 2017-05-21 10:00:00 宇通 YT01SOC 70%,车速 52.3 km/h
2323020000010203040506070809101112131415160100352016051510000001010012350000037102710000A064010505050A0005010A00102003E8...
```
## 新增样本步骤
1. 从生产抓包工具tcpdump / rg-samples导出一帧完整字节`0x23 0x23` 起始和 BCC 尾)
2. VIN 替换为测试段:`LTEST0000<9位序号>`
3. 放入本目录,文件名按规范
4.`Gb32960DecoderGoldenTest` 中添加断言(期望 VIN / 命令 / 关键字段)
5. 运行 `mvn -pl protocol-gb32960 test`
## 验证目标
- 100% 解析一致性(新服务解析结果 === 旧服务解析结果)
- BCC 校验通过
- 事件映射字段单位一致(车速 km/h、里程 km、经纬度十进制度

View File

@@ -0,0 +1 @@
232307fe4c54455354303030303030303030303131010000a2

View File

@@ -0,0 +1 @@
232307fe4c54455354303030303030303030303231010000a1

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303030310102101a040d0c213901010302037a000531c2157825fb44012e27103400020101025575304eca6b157a288b030c82050a00aa0002686c02940200000100630101050006c4f414015eedea06018c0f0701310ebf01014a01054907000000000000000000080101157825fb00900001900eea0eea0eea0ee60ee60ee60ef00eef0ef10eeb0eec0eeb0eed0eee0eee0eeb0eed0eea0ed30ed20ed20ed30ed50ed40ecc0ecd0ecc0ece0ece0ece0ed40ed40ed40ed30ed10ed00ed20ed10ed00ece0ece0ecf0ed40ed40ed40eda0eda0edb0ebf0ed40ed30ed60ed60ed50ed40ed50ede0edf0ede0ede0eee0eed0eec0ee80ee80ee50ef00eef0eef0ee80ee90eea0ee40ee70ee80eeb0eeb0eea0eee0eef0eef0ef60ef60ef50ef80ef70ef50ef60ef50ef60eef0ef00ef10ef60ef80ef90efa0ef70ef90efa0ef80ef80efc0efc0efc0ef50ef50ef50ef20ef30ef40ef50ef50ef40ef50ef50ef70ef40ef40ef50ef80ef80ef70ef70ef50ef40ef70ef70ef70efa0efd0efc0ef70ef80ef70ef80ef60ef50f050f070f070ef00eed0ef009010100084a4a4a4a494a4a493001026c08fcffffff0003003a02ee02e402e5000000000031010c800006ffffffff0c80ffffffff0088320c8004c2159002c35333ffffffffff34ffffffffff001c8000096c0c802742ffffffff8300250019000e0b002300020000000000014f00ffffffffffff00000282ffffffff1ffe1fedff1fa0

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303030320102ed1a040d0c213a01010302013600036758162826284c012e2710010002010101485bf84de44b165526ec030da9017400d200026a6b023a0200000100b101010402ffff0015050107373d6c01d2e6880601020f8701520efe010141010540070000000000000000000801011628262800900001900f860f870f860f6d0f6c0f6c0f870f860f870f6d0f700f6e0f7e0f7d0f710f640f630f650f6e0f800f7e0f260f250f250f810f810f820f620f660f670f810f820f830f670f680f670f780f770f770f670f660f670f640f630f620f1a0f1c0f1c0f500f500f4d0f690f6b0f690f540f530f5a0f420f440f440f6e0f6e0f6d0f460f450f450f720f6c0f6b0f650f640f670f500f4f0f4c0f150f140f150f780f760f780efe0eff0eff0f7b0f7c0f7d0f640f640f640f6e0f6d0f6e0f360f390f390f5c0f5e0f5c0f650f660f660f7e0f800f800f640f620f640f810f800f800f300f330f2f0f800f820f830f670f660f680f820f840f820f650f670f660f820f840f830f680f660f660f820f810f810f680f660f660f720f720f710f680f670f67090101000841414141404040403001026b092eff00ff000100730ca80c800ca4006c00016c0ca40ca80ca30ca30ca30ca30ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca40ca431010dc00005ffffffff0dc0ffffffff008a320dc000ff15e7009e4833ffffffffff34ffffffffff002f8000096b0dc02715000d000e83002402040100002700080000000000c900000000ffffffffffffffffffffffff1ffe15c8631f18

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303030330102ed1a040d0c2200010203010000000418d914ed27103a020007d0000002010104454e204e204814be2710030006000000b400024a4902bc02000001006701000400ffff0012050006be89500161217106013d0e91012c0e860101470105460700000000000000000008010114ed271000900001900e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e8a0e870e870e880e880e880e880e880e880e880e880e880e870e860e860e880e880e8a0e880e880e880e880e880e880e880e880e880e880e880e880e910e900e900e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e880e88090101000847474747464746463001004907c6ff00ff00000039000000000004006c00016c00000004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400000004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040005000500050005000500050005000531010eb00005ffffffff0eb0ffffffff0081320eb01b3b14e613234733ffffffffff34ffffffffff001d800009490eb02710000d000e83002402040100002700080000000000d500000000ffffffffffffffffffffffff1ffe167c021f7b

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303031300102ed1a040d0c220501020301006400049992163027df4f012e27101e00020101015952da51365215f4279f030005000000000002625f02a80200000100a801000400ffff0000050106c3d5cf016240a406010b0f90015b0f4001014a01054907000000000000000000080101163027df00900001900f8c0f8d0f8d0f8d0f8e0f8e0f8f0f8f0f8e0f8f0f900f900f8f0f900f7a0f7c0f800f800f7f0f7c0f7c0f7c0f7e0f7e0f810f7f0f7f0f7e0f820f830f500f500f500f840f850f840f840f820f850f850f820f820f730f750f730f690f6a0f680f620f5f0f600f500f500f500f630f5a0f6d0f6c0f6c0f6d0f500f500f500f6b0f6b0f6c0f6a0f5d0f5f0f5c0f5f0f610f5c0f5e0f5a0f5c0f5c0f610f630f630f640f510f520f540f500f500f500f610f550f500f400f430f410f500f510f500f4e0f4f0f530f500f500f4f0f550f550f560f550f570f560f540f570f560f510f540f530f540f560f5a0f5a0f590f570f7e0f7e0f810f580f4e0f530f570f550f510f500f480f4d0f870f7e0f7e0f7c0f7e0f7f0f7c0f7b0f810f7d0f7e0f7f09010100084a4a4a4a494949493001005f07daff00ff00000038000000000004006c00016c0000000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400000004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000400040004000500050005000500050005000500053101003c0005ffffffff003cffffffff008a32003c00000000ffff4f33ffffffffff34ffffffffff002c8000095f003c2710000d000e830024020401000027000800000000000000000000ffffffffffffffffffffffff1ffe17ae631ff9

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303032330101d61a040d0c22070101030203010004c1ee17fc261337012e271032000201010157705a4fe26e17f2260c0309d50bba021a00027f6e022b030000010106010105000733a7d401d832920601840eda011c0ebf01044b0101480700000000000000000008010117fc261300a20001a20ecc0eca0ec30ecb0ec90ec70eca0ec90ec20ec20ec40ec20ec70eca0eca0ec90ece0ecd0ec80ecd0eca0ecc0ec90ec80ece0ec70ec50ebf0ed40ed20ed40ed30ed40ed30ed50ed40ed30ed50ed20ed50ed50ed30ecc0ecb0ec80ecf0ec90ed00ec70ecd0ec70ec50ec70ece0eca0eca0ec90eca0ed10ec90ed00ecf0eca0ed10ed30ed10ecf0ed20ed00ed20ed40ed50ed20ed20ed60ed20ed30ed20ed50ed50ed70ed40ed10ed40ed30ed20ed30ed20ecc0ed00ed10ecc0ed30ed10ed00ecb0ec90ecd0ec80ed10ec50ec80ed20ec10ec90ec50ec90ec30ec50ed40ed40ed40ed40ed40ed40ed60ed50ed30ed50ed50ed50ed50ed40ed50ed40ed30ed20ed20ed30ed60ed50eda0ed00ed70ed80ed60ed70ed50ed60ece0ed50ed40ed60ed40ed10ed30ed20ed40ed40ed40ecf0ed80ed60ed50ed60ed70ed60ed60ed40ed50ed70eda0901010020484a4a4b4b4b4b4a484b4b4b4b4b4b4a484a4b4b4b4b4b4a484a4b4b4b4b4b4acc

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303130370101ae1a040d0c39263001016908fc09c4410003007403200320032001b000c9c803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e8cb

View File

@@ -0,0 +1 @@
232302fe4c544553543030303030303030303231310102ed1a040d0c3a120101030200000002533c150727203e01002710000002010104464e204e204814fc2710030004000200be00025353023a01000001010201010402ffff00130500072eb49401de012e06013f0ea7012a0e9301014001033f070000000000000000000801011507272000900001900ea60ea60ea60ea60ea60ea60ea50ea60ea60ea60ea60ea60ea60ea50e960e990e960e980e980e990e970e990e970e960e980e980e990e980e950e970e950e940e950e960e970e940e990e960e960e950e950e930e960e9b0e9c0e9c0e990e9b0e940e950e930e980e9a0e9a0e940e950ea50ea50ea50ea50ea50ea60ea70ea60ea50ea50ea50e9d0e9e0e9e0e9e0e990e970e970e990e980e9a0e9d0e9a0e9c0e9a0e990e9a0e9a0e9b0e9c0e9c0e980e950e980e990e990e990e980e970e970e980e970e970e990e990e9a0e990e990e990e9a0e980e960e960e990e970e970e970e970e980e950e930e9b0e980e9a0e990e9a0e990e940e940e950e940e950e950e990e990e9b0e9a0e9b0e9b0e960e950e960e950e950e950e950e940e99090101000840403f403f3f3f3f300102530708ff00ff00000037000000000000006c00016c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000031010e880005ffffffff0e88ffffffff008a320e880002150e00014733ffffffffff34ffffffffff0044800009530e882710000d000e830024020401000027000800000000005401000000ffffffffffffffffffffffff1ffe1d7b011fdd

View File

@@ -0,0 +1 @@
232303fe4c544553543030303030303030303032320102101a040d0d0b0a010203010000000532ad156d271c45011007d0006502010103514e204e205c003f27100300070000000000025e5c03b601000001014d0100050106c3aa530160540f0601010ee301120ede01014801054707000000000000000000080101156d271c00900001900ee30ee30ee30ee00ee20ee20ee20ee10ee20ee30ee20ee20ee20ee10ee30ee10ee10ede0ee10ee10ee10ee00ee10ee10ee10ee00ee00ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee10ee1090101000848484848474848473001005c0816ffffff0000003a000a0000000100000000003101003c0005ffffffff003cffffffff008b32003c00000000ffff5233ffffffffff34ffffffffff004c8000095c003c2710ffffffff8300250019000e0b002300020000000000000000ffffffffffff0000000affffffff1ffe1fedff0089

View File

@@ -0,0 +1 @@
232303fe4c544553543030303030303030303230390101ae1a040d0c391b3001005307ee09c4410037000100000000000001b000c9c803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e803e8be

View File

@@ -0,0 +1 @@
232301fe4c5445535430303030303030303030313901001e1a040d0d0b1a000c38393836303332313435323039303735363530390100bd