feat: make gb32960 archive history query production ready

This commit is contained in:
kkfluous
2026-06-23 11:55:44 +08:00
parent b14871ff1c
commit ba68ffe061
462 changed files with 23639 additions and 2341 deletions

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lingniu.ingest</groupId>
<artifactId>lingniu-vehicle-ingest</artifactId>
<version>0.1.0-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<artifactId>ingest-codec-common</artifactId>
<name>ingest-codec-common</name>
<description>BCD / CRC / BCC / bit utils 等公共编解码工具。</description>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,19 @@
package com.lingniu.ingest.codec;
/** BCC异或校验。GB/T 32960 与 JT/T 808 都使用此方式做帧尾校验。 */
public final class BccChecksum {
private BccChecksum() {}
public static byte compute(byte[] data, int offset, int length) {
byte sum = 0;
for (int i = offset; i < offset + length; i++) {
sum ^= data[i];
}
return sum;
}
public static boolean verify(byte[] data, int offset, int length, byte expected) {
return compute(data, offset, length) == expected;
}
}

View File

@@ -0,0 +1,30 @@
package com.lingniu.ingest.codec;
/** BCD (Binary-Coded Decimal) 编解码。JT/T 808 的手机号、时间戳常用。 */
public final class BcdCodec {
private BcdCodec() {}
public static byte[] encode(String decimal) {
int len = (decimal.length() + 1) / 2;
byte[] out = new byte[len];
int idx = decimal.length() % 2;
if (idx == 1) {
out[0] = (byte) Character.digit(decimal.charAt(0), 10);
}
for (int i = idx; i < decimal.length(); i += 2) {
int hi = Character.digit(decimal.charAt(i), 10);
int lo = Character.digit(decimal.charAt(i + 1), 10);
out[(i + idx) / 2] = (byte) ((hi << 4) | lo);
}
return out;
}
public static String decode(byte[] bcd) {
StringBuilder sb = new StringBuilder(bcd.length * 2);
for (byte b : bcd) {
sb.append((b >> 4) & 0x0F).append(b & 0x0F);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,34 @@
package com.lingniu.ingest.codec;
/** 位运算工具:从字节数组读写无符号整数。 */
public final class BitUtils {
private BitUtils() {}
public static int readUint8(byte[] buf, int offset) {
return buf[offset] & 0xFF;
}
public static int readUint16BE(byte[] buf, int offset) {
return ((buf[offset] & 0xFF) << 8) | (buf[offset + 1] & 0xFF);
}
public static long readUint32BE(byte[] buf, int offset) {
return ((long) (buf[offset] & 0xFF) << 24)
| ((long) (buf[offset + 1] & 0xFF) << 16)
| ((long) (buf[offset + 2] & 0xFF) << 8)
| (buf[offset + 3] & 0xFF);
}
public static void writeUint16BE(byte[] buf, int offset, int value) {
buf[offset] = (byte) ((value >> 8) & 0xFF);
buf[offset + 1] = (byte) (value & 0xFF);
}
public static void writeUint32BE(byte[] buf, int offset, long value) {
buf[offset] = (byte) ((value >> 24) & 0xFF);
buf[offset + 1] = (byte) ((value >> 16) & 0xFF);
buf[offset + 2] = (byte) ((value >> 8) & 0xFF);
buf[offset + 3] = (byte) (value & 0xFF);
}
}

View File

@@ -0,0 +1,28 @@
package com.lingniu.ingest.codec;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class BcdCodecTest {
@Test
void roundTripEvenLength() {
// 偶数位直接等值
byte[] bcd = BcdCodec.encode("1380013800");
assertThat(BcdCodec.decode(bcd)).isEqualTo("1380013800");
}
@Test
void roundTripOddLengthPadsLeadingZero() {
// 奇数位会在高位补 0
byte[] bcd = BcdCodec.encode("13800138000");
assertThat(BcdCodec.decode(bcd)).isEqualTo("013800138000");
}
@Test
void checksumMatches() {
byte[] data = {0x01, 0x02, 0x03, 0x04};
assertThat(BccChecksum.compute(data, 0, data.length)).isEqualTo((byte) 0x04);
}
}