Files
lingniu-vehicle-ingest/CHANGELOG.md
2026-07-01 18:22:51 +08:00

114 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
本文件记录本仓库的显著变更。日期使用 `YYYY-MM-DD` 格式。
格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本遵循 [SemVer](https://semver.org/lang/zh-CN/)。
> 说明v0.1.0 为本仓库 git 初始化时的基线快照。下面列出的"主题改动"实际上都被压在
> `chore: initial import of lingniu-vehicle-ingest` 这一个 commit 里——这是 git
> 初始化前已经完成的工作,无法事后拆分。从下一次提交开始走"小步分主题提交"流程。
---
## [Unreleased]
### Added —— 原始报文冷存Raw Archive接入 EventSink 扇出
- 新增 `VehicleEvent.RawArchive` sealed 变体(`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 语义覆盖
- 失败 completeExceptionallyEventBus 打 WARN
- AutoConfig 在 `ArchiveStore` bean 存在时自动装配
- `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.Raw` record 结构**未变**,下游 `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`(默认 60s0 禁用)。
- 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` 移除 `gb32960V2016FuelCellStack` bean。
- `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 预留区段。
建议推动对端:
1. 切换到 V2025 帧头(`2424`
2. 或迁移到 `0x80~0xFE` 用户自定义区段;
3. 或提供私有协议文档,再决定是否实现 vendor-specific parser。
- `Gb32960BodyParser` 当前对 unknown 块的策略是"吞掉剩余所有字节为单个 Raw 后终止
循环",未做单块异常隔离。后续可改为对每个 parser.parse 调用加 try/catch +
position 回滚,实现单块失败不拖垮整帧。
- `platformAuthorizer.authenticate` 是 EventLoop 同步调用,存在 worker 阻塞风险,
建议挪到独立业务线程池(与 0x30 议题无关,但属于同模块潜在风险)。
[0.1.0]: #010--2026-04-15