6.7 KiB
6.7 KiB
Changelog
本文件记录本仓库的显著变更。日期使用 YYYY-MM-DD 格式。
格式参考 Keep a Changelog,版本遵循 SemVer。
说明:v0.1.0 为本仓库 git 初始化时的基线快照。下面列出的"主题改动"实际上都被压在
chore: initial import of lingniu-vehicle-ingest这一个 commit 里——这是 git 初始化前已经完成的工作,无法事后拆分。从下一次提交开始走"小步分主题提交"流程。
[Unreleased]
Added —— 原始报文冷存(Raw Archive)接入 EventSink 扇出
- 新增
VehicleEvent.RawArchivesealed 变体(ingest-api),携带一帧入站的原始字节 +command/infoType供按协议命令分片归档。 Dispatcher.dispatch在 interceptor 之前按RawFrame.rawBytes()产出一条RawArchive事件,保证 dedup / rate-limit 不过滤 raw archive,满足"每帧必留痕"目标。- 新增
ArchiveEventSink implements EventSink(sink-archive):accepts仅VehicleEvent.RawArchive- 写入 key
yyyy/MM/dd/<source>/<vin>/<eventId>.bin(UTC 日期分片) - 同 eventId 重复写按 put 语义覆盖
- 失败 completeExceptionally,EventBus 打 WARN
- AutoConfig 在
ArchiveStorebean 存在时自动装配
KafkaEventSink.accepts(RawArchive)返回false:本期 Kafka 不投递原始字节, 只落 ArchiveStore。未来需要 Kafka 回填 URI 时再开(TopicRouter/EnvelopeMapper已补好 switch 分支)。- 覆盖测试
ArchiveEventSinkTest—— accepts 过滤 / 稳定 key 写入 / 幂等覆盖 3 case。
Changed —— GB/T 32960 Body Parser 单块异常隔离
Gb32960BodyParser主循环对单信息块的 parser 异常不再放弃整帧:- 固定长度块(
fixedLength ≥ 0):回滚 reader → 按fixedLen截取字节兜成InfoBlock.Raw→continue继续解析后续块; - 变长块(
fixedLength == -1)或剩余字节不足:剩余字节全部兜成Raw后终止循环; parser契约违反(声明fixedLen=X但实际读了Y)仍抛DecodeException, 以暴露 parser bug,不走 lenient 兜底,避免静默误解析长期误判;- 只捕获
DecodeException/BufferUnderflowException/IndexOutOfBoundsException,RuntimeException继续向上抛,保留 bug 可见性。
- 固定长度块(
- 新增配置
lingniu.ingest.gb32960.parse.lenient-block-failure(默认true); 需要回退严格模式时设为false。 InfoBlock.Rawrecord 结构未变,下游Gb32960EventMapper按findBlock(Class)类型匹配,新增 Raw 不影响业务事件映射。- 新增测试
Gb32960BodyParserIsolationTest—— 3 种隔离场景 + 严格模式回退,共 4 case。
0.1.0 — 2026-04-15
初始基线。多模块 Spring Boot 车辆遥测接入服务。
Added
- GB/T 32960.3 接入(
protocol-gb32960):Netty server、帧分帧器、消息解码器、 V2016/V2025 双版本信息体 parser、平台登入鉴权、VIN 白名单、读空闲告警。 - JT/T 808 / JT/T 1078 / JSATL12 接入(
protocol-jt808/protocol-jt1078/protocol-jsatl12)。 - MQTT 入站(
inbound-mqtt)。 - Disruptor 事件总线(
ingest-core):高吞吐解耦解码与下游 sink。 - Sink:本地归档(
sink-archive)、Kafka(sink-kafka,含 protobuf envelope)。 - 会话状态(
session-core)。 - 可观测性:Micrometer/Actuator 装配(
observability)。 - 终端控制命令网关(
command-gateway)。
Removed
- 旧文件型事件索引模块和 DuckDB 依赖;历史查询统一收敛到 TDengine 与
archive://...原始报文引用。
Fixed —— GB/T 32960 应答帧时间字段
Gb32960MessageDecoder对VEHICLE_LOGIN/VEHICLE_LOGOUT/PLATFORM_LOGIN/PLATFORM_LOGOUT命令预解析 body 首 6B 采集时间,写入header.eventTime。修复前 handler 回 ACK 时落到Instant.now(),导致应答时间与原 报文时间不一致,违反 §6.3.2 应答规则。
Added —— Netty 读空闲告警
- 配置项
lingniu.ingest.gb32960.idle-read-seconds(默认 60s,0 禁用)。 - pipeline 注入
IdleStateHandler(idleReadSeconds, 0, 0),仅打 WARN 日志不断链, 用于排查"对端登入成功后长时间不推数据"场景。
Changed —— GB32960 信息体 parser 包结构重构
- 原
codec/parser/下的 16 个共用类按版本严格拆分到parser/v2016/(10 类) 和parser/v2025/(11 类)两个独立子包。 - 取消
Vehicle/DriveMotor/Engine/Position的v2016()/v2025()工厂方法,每个 版本独立类,零代码共用。 Alarm解析中跨版本静态调用readFaultList也已改为各版本类内部私有方法。Gb32960AutoConfiguration同步更新所有 import 与@Bean工厂。
Removed —— 违反 2016 标准的 typeCode 注册
- 经核对 GB/T 32960.3-2016 附录 B 表 B.3,
0x30~0x7F整段为预留区, 2016 标准里没有 0x30 / 0x31 / 0x32 任何字段定义。 - 删除
FuelCellStackV2016BlockParser(曾把 V2025 字段布局硬套到 V2016 帧的 0x30 段, 解出来的电压/电流远超规范上限,是非物理值)。 Gb32960AutoConfiguration移除gb32960V2016FuelCellStackbean。Gb32960DecoderGoldenTest中 749B 真实生产帧的断言由 "isEmpty()(不应有 Raw 兜底)"翻转为 "isPresent()(peer 越界使用 0x30+ 预留 typeCode,应有 Raw 兜底)"。这才是符合 2016 国标的正确预期。
Diagnostics —— BodyParser hex dump 增强
Gb32960BodyParser在遇到未知 typeCode 触发 unknown 路径时,额外打一条 WARN, 内容为整段 info-block 区域的 16 字节/行 hex dump,并在typeStartPos字节后插入<标记,便于人工反查上一个块的字段对齐。
Notes —— 待办
- peer 在 V2016 帧(
2323起始)里下发 0x30/0x31/0x32 是越界使用 2016 预留区段。 建议推动对端:- 切换到 V2025 帧头(
2424); - 或迁移到
0x80~0xFE用户自定义区段; - 或提供私有协议文档,再决定是否实现 vendor-specific parser。
- 切换到 V2025 帧头(
Gb32960BodyParser当前对 unknown 块的策略是"吞掉剩余所有字节为单个 Raw 后终止 循环",未做单块异常隔离。后续可改为对每个 parser.parse 调用加 try/catch + position 回滚,实现单块失败不拖垮整帧。platformAuthorizer.authenticate是 EventLoop 同步调用,存在 worker 阻塞风险, 建议挪到独立业务线程池(与 0x30 议题无关,但属于同模块潜在风险)。