# Go Vehicle Gateway 旁路验证记录 验证时间:2026-07-01 22:00-22:10 CST ## 部署版本 - Git commit:`60ca42e` - 镜像:`crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-60ca42e-20260701220622` - 部署方式:ECS `115.29.187.205` 上 Docker Compose 旁路部署 - Compose 目录:`/opt/lingniu-go` ## 旁路端口和 topic 为了不影响现有 Java 生产服务,本次没有占用生产端口和生产 topic。 | 项 | 值 | |---|---| | GB32960 旁路 TCP | `115.29.187.205:23296 -> container :32960` | | JT808 旁路 TCP | `115.29.187.205:18080 -> container :808` | | Realtime API | `115.29.187.205:20210` | | GB32960 RAW topic | `vehicle.raw.go.gb32960.v1` | | JT808 RAW topic | `vehicle.raw.go.jt808.v1` | | Yutong MQTT RAW topic | `vehicle.raw.go.yutong-mqtt.v1` | | Unified topic | `vehicle.event.go.unified.v1` | ## 容器状态 ECS 上四个 Go 容器均已启动: ```text go-vehicle-gateway Up go-history-writer Up go-stat-writer Up go-realtime-api Up ``` ## Kafka 验证 向 `18080` 注入 JT808 样例帧后,`vehicle.raw.go.jt808.v1` 和 `vehicle.event.go.unified.v1` 均消费到统一 JSON envelope。 样例解析结果包含: - `protocol=JT808` - `message_id=0x0200` - `phone=013307795425` - `longitude=121.069881` - `latitude=30.590151` - `speed_kmh=23` - `total_mileage_km=10241.2` - 表 27 附加项 `0x01` 原始值 `0001900C` ## TDengine 验证 目标库:`lingniu_vehicle_ts` ```text raw_frames count = 3 vehicle_locations count = 2 vehicle_mileage_points count = 2 ``` 最新 RAW 标识: ```text GB32960 | LNBSCB3D4R1234567 | LNBSCB3D4R1234567 | JT808 | JT808:013307795425 | | 013307795425 JT808 | JT808:013307795425 | | 013307795425 ``` 说明:JT808 样例 phone 当前没有在 `vehicle_identity_binding` 中解析到 VIN,因此 TDengine tag 使用 `vehicle_key=JT808:013307795425`。 ## Redis 验证 向 `23296` 注入带 VIN 的 GB32960 合成帧后,Realtime API 可查询: ```text GET /api/realtime/vehicles/LNBSCB3D4R1234567 ``` 返回字段包含: - `vin=LNBSCB3D4R1234567` - `protocols=["GB32960"]` - `online=true` - `speed_kmh=30` - `total_mileage_km=10000` - `soc_percent=85` - `longitude=121` - `latitude=30.56` ## MySQL 统计验证 向 `23296` 注入带 VIN 的 GB32960 合成帧后,`vehicle_daily_metric` 写入: ```text LNBSCB3D4R1234567 | 2020-07-01 | GB32960 | daily_mileage_km | 0.000 | 10000.000 | 10000.000 | 1 LNBSCB3D4R1234567 | 2020-07-01 | GB32960 | daily_total_mileage_km | 10000.000 | 10000.000 | 10000.000 | 1 ``` 说明:本次 GB32960 合成帧的设备时间为测试值,因此统计日期为 `2020-07-01`。 ## 已修复问题 - TDengine WebSocket 普通 `INSERT` 使用参数占位符时,字符串未按预期加引号,导致语法错误。已在 `60ca42e` 中改为显式 SQL literal 转义。 - MySQL Go DSN 中 `charset=utf8mb4%2Cutf8` 会被 driver 传给 MySQL,导致语法错误;ECS 旁路 env 已改为 `charset=utf8mb4`。 - TDengine ECS 当前可用 root 密码与早先记录不一致,本次旁路 env 已按实际可认证值修正。 ## 未完成 - 未切换真实生产端口 `32960/808`。 - 未将真实外部 32960/808 流量转发到 Go 旁路端口。 - 未开启宇通 MQTT 正式订阅。 - JT808 样例未解析到 VIN,需维护 `vehicle_identity_binding` 后再验证 808 每日统计。 ## 2026-07-01 22:22 真实归档帧复验 部署版本已更新: - Git commit:`5f4f4fd` - 镜像:`crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-5f4f4fd-20260701222128` 本轮补充验证内容: - JT808 支持 2019 版 versioned header,真实帧 `0x0200` 可解析出 `phone=14894135060`、`longitude=117.178187`、`latitude=30.570155`、`speed_kmh=33.4`、`total_mileage_km=7868.9`。 - GB32960 真实燃料电池帧 `VIN=LB9A32A21R0LS1707` 可解析 `0x01` 整车、`0x02` 驱动电机、`0x03` 燃料电池、`0x04` 发动机、`0x05` 位置、`0x06` 极值、`0x07` 报警、`0x08` 电压、`0x09` 温度;后续 `0x30` 广东燃料电池扩展暂以 unknown raw 保留。 - GB32960 真实帧字段包含 `fuel_cell_hydrogen_consumption_kg_per_100km=1.8`、`longitude=120.800326`、`latitude=31.634907`、`total_mileage_km=53490.9`。 - Realtime API `GET /api/realtime/vehicles/LB9A32A21R0LS1707` 已返回上述经纬度和氢耗字段。 - TDengine `vehicle_locations` 和 `vehicle_mileage_points` 已写入真实 GB32960 位置和里程点: ```text 2020-07-01 08:09:22 | GB32960 | LB9A32A21R0LS1707 | lon=120.800326 | lat=31.634907 | mileage=53490.9 ``` 后续仍需补齐: - JT808 真实 phone `14894135060` 仍未解析到 VIN,需维护身份绑定后再验证 VIN 维度实时和统计。 ## 2026-07-01 22:28 广东燃料电池扩展复验 部署版本已更新: - Git commit:`a15de91` - 镜像:`crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-a15de91-20260701222743` 本轮补充验证内容: - GB32960 真实燃料电池帧 `VIN=LB9A32A21R0LS1707` 的 vendor 尾部已从 `unknown_unit` 补为结构化块: `0x30 gd_fc_stack`、`0x31 gd_fc_auxiliary`、`0x32 gd_fc_dcdc`、`0x33 gd_fc_air_conditioner`、 `0x34 gd_fc_vehicle_info`、`0x80 gd_fc_demo_extension`、`0x83 gd_fc_vendor_tlv`。 - Kafka `vehicle.raw.go.gb32960.v1` 最新消息包含上述 block,并输出关键扁平字段: `gd_fc_stack_hydrogen_inlet_pressure_kpa=97`、`gd_fc_stack_cell_count=108`、 `gd_fc_vehicle_hydrogen_mass_kg=4.3`、`gd_fc_demo_stack_temp_c=65`。 - Realtime API `GET /api/realtime/vehicles/LB9A32A21R0LS1707` 已返回 vendor 扁平字段: ```text gd_fc_dcdc_controller_temp_c=47 gd_fc_demo_stack_temp_c=65 gd_fc_stack_cell_count=108 gd_fc_stack_frame_cell_count=108 gd_fc_stack_hydrogen_inlet_pressure_kpa=97 gd_fc_stack_water_outlet_temp_c=65 gd_fc_vehicle_hydrogen_mass_kg=4.3 ``` - TDengine `raw_frames` 中 GB32960 行数增加到 `4`,最新 RAW 标识: ```text 2026-07-01 14:28:24.869 | 172.20.0.1:47082 | GB32960 | LB9A32A21R0LS1707 | OK ``` 后续仍需补齐: - JT808 真实 phone `14894135060` 到 VIN 的绑定数据维护与统计复验。 - Go 旁路尚未切换到生产 `32960/808` 端口;仍运行在 `23296/18080`。 ## 2026-07-01 22:43 JT808 前导零 phone 身份解析复验 部署版本已更新: - Git commit:`6b55694` - 镜像:`crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-6b55694-20260701224300` 本轮修复内容: - JT808 2011 老版包头的终端手机号按 BCD[6] 解析会保留前导 `0`,例如包头为 `013079963379`。 - `vehicle_identity_binding.phone` 中实际维护的是去前导零后的手机号 `13079963379`。 - 身份解析候选键已调整为先查原始 phone,再查去前导零 phone,之后再按 `device_id`、`plate` 降级。 真实帧复验样本: - 归档帧:`/opt/lingniu/vehicle-ingest/gb32960/archive/2026/07/01/JT808/unknown/1782903942845000.bin` - 消息:`0x0200` 位置信息汇报 - 包头 phone:`013079963379` - 绑定表 phone:`13079963379` - 解析 VIN:`LKLG7C4E3NA774736` Kafka `vehicle.raw.go.jt808.v1` 最新消息确认: ```text vin=LKLG7C4E3NA774736 phone=013079963379 parsed.header.phone_trim_zero=13079963379 parsed.identity.resolved=true parsed.identity.source=phone parsed.identity.value=13079963379 ``` Realtime API 确认: ```text GET /api/realtime/vehicles/LKLG7C4E3NA774736 longitude=119.557997 latitude=29.049092 speed_kmh=0 total_mileage_km=0 device_time=2026-07-01T19:05:40+08:00 ``` TDengine 确认: ```text raw_frames: 2026-07-01 14:49:10.913 | JT808 | LKLG7C4E3NA774736 | phone=013079963379 | message_id=512 vehicle_locations: 2026-07-01 11:05:40.000 | JT808 | LKLG7C4E3NA774736 | lon=119.557997 | lat=29.049092 | speed=0 | mileage=0 vehicle_mileage_points: 2026-07-01 11:05:40.000 | JT808 | LKLG7C4E3NA774736 | mileage=0 ``` MySQL `vehicle_daily_metric` 确认: ```text LKLG7C4E3NA774736 | JT808 | 2026-07-01 | daily_mileage_km | 0.000 | sample_count=1 | TOTAL_MILEAGE_DIFF LKLG7C4E3NA774736 | JT808 | 2026-07-01 | daily_total_mileage_km | 0.000 | sample_count=1 | TOTAL_MILEAGE_DIFF ``` 说明:该真实位置帧携带的表 27 附加项 `0x01` 总里程值为 `0`,因此本次只验证身份解析、链路落地和差值统计写入;要验证非零日里程,需要回放或等待同一 VIN 的非零总里程连续位置点。 ## 2026-07-01 22:55 Kafka 发布重试部署复验 部署版本已更新: - Git commit:`7e7066d` - 镜像:`crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-7e7066d-20260701225503` 本轮修复内容: - gateway 的 Kafka 发布增加同步重试封装。 - 默认配置: - `KAFKA_PUBLISH_ATTEMPTS=3` - `KAFKA_PUBLISH_BACKOFF_MS=100` - `KAFKA_PUBLISH_TIMEOUT_MS=3000` - RAW 仍先于 unified event 写入;RAW 写失败不会继续写 unified。 - 单元测试覆盖首次失败后成功、重试耗尽返回错误、上下文取消时停止重试。 部署后容器状态: ```text go-vehicle-gateway | go-7e7066d-20260701225503 | Up go-history-writer | go-7e7066d-20260701225503 | Up go-stat-writer | go-7e7066d-20260701225503 | Up go-realtime-api | go-7e7066d-20260701225503 | Up ``` 真实 808 帧回放复验: ```text source_endpoint=172.20.0.1:51966 protocol=JT808 message_id=0x0200 phone=013079963379 vin=LKLG7C4E3NA774736 parsed.identity.value=13079963379 ``` Kafka `vehicle.raw.go.jt808.v1` 确认新消息写入,partition 6 offset 从 `1` 增加到 `2`。最新消息包含: ```text received_at_ms=1782917751903 source_endpoint=172.20.0.1:51966 vin=LKLG7C4E3NA774736 fields.longitude=119.557997 fields.latitude=29.049092 fields.total_mileage_km=0 ``` TDengine `raw_frames` 确认新 RAW 落地: ```text 2026-07-01 14:55:51.903 | JT808 | LKLG7C4E3NA774736 | phone=013079963379 | source=172.20.0.1:51966 ``` Realtime API 确认 Redis 已刷新: ```text GET /api/realtime/vehicles/LKLG7C4E3NA774736 received_at_ms=1782917751903 source_endpoint=172.20.0.1:51966 longitude=119.557997 latitude=29.049092 total_mileage_km=0 ``` 说明:当前重试层只能覆盖 Kafka 短暂抖动;如果 Kafka 长时间不可用,仍需后续增加本地磁盘 spool/WAL 和恢复补发。 ## 2026-07-01 23:02 切换 Go 到生产 32960/808 端口 本轮操作: - 停止 ECS 上原有 Java Docker 容器: - `gb32960-ingest-app` - `jt808-ingest-app` - `yutong-mqtt-app` - `vehicle-history-app` - `vehicle-analytics-app` - `vehicle-state-app` - `telemetry-field-parser-app` - 保留 Go sidecar 和 Portainer agent。 - 修改 `/opt/lingniu-go/.env`: - `GO_GB32960_TCP_PORT=32960` - `GO_JT808_TCP_PORT=808` - Go gateway 使用镜像: - `crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-243de5c-20260701230028` - 开启 Kafka 本地 spool: - host:`/opt/lingniu-go/spool/gateway` - container:`/data/spool/gateway` 端口验证: ```text 0.0.0.0:32960 -> go-vehicle-gateway:32960 0.0.0.0:808 -> go-vehicle-gateway:808 18080/23296 -> 已释放 ``` 启动日志确认: ```text kafka durable spool enabled dir=/data/spool/gateway replay_interval_ms=1000 tcp listener started protocol=GB32960 addr=[::]:32960 tcp listener started protocol=JT808 addr=[::]:808 ``` 真实连接确认: ```text GB32960 external remote: 8.134.95.166:50084 JT808 external remote: 115.231.168.135:13801 ``` 生产 `808` 回放复验: ```text source_endpoint=172.20.0.1:54680 vin=LKLG7C4E3NA774736 phone=013079963379 longitude=119.557997 latitude=29.049092 total_mileage_km=0 ``` TDengine 最新 RAW 复验显示生产 808 已有真实外部数据持续落地: ```text 2026-07-01 15:03:02.960 | JT808 | LKLG7C4E1NA774802 | 013079963291 | 222.66.200.68:28394 2026-07-01 15:03:02.114 | JT808 | | 14894135583 | 122.152.221.156:33871 2026-07-01 15:03:01.352 | JT808 | | 013307795519 | 115.231.168.135:13801 2026-07-01 15:03:01.330 | JT808 | LKLG7C4E3NA774736 | 013079963379 | 222.66.200.68:28393 ``` Kafka spool 目录确认: ```text /opt/lingniu-go/spool/gateway file count = 0 ``` 说明:生产 808 中仍有部分 phone 未解析出 VIN,后续需要继续维护 `vehicle_identity_binding` 的 phone/device_id/plate 到 VIN 映射,或者增强 808 注册/鉴权帧中的 identity 回写。 ## 2026-07-01 23:09 808 里程统计防 0 污染复验 部署版本已更新: - Git commit:`d347525` - 镜像:`crpi-85r4m0ackrm3qpje.cn-shanghai.personal.cr.aliyuncs.com/oneos/vehicle-gateway-go:go-d347525-20260701230845` 本轮修复内容: - `stat-writer` 不再把 `total_mileage_km <= 0` 的采样写入 MySQL 每日指标。 - MySQL upsert 对历史 `first_total_mileage_km=0` / `latest_total_mileage_km=0` 做纠偏;后续首次有效总里程会替换掉历史 0。 - `realtime-api` 合并实时快照时,非正 `total_mileage_km` 不再覆盖已有正值;速度、位置、状态等其他字段仍按事件时间更新。 测试验证: ```text go test ./... GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./cmd/gateway GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./cmd/history-writer GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./cmd/stat-writer GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./cmd/realtime-api ``` 生产回放验证: - 第一条 808 帧:`device_time=2026-07-01T23:09:35+08:00`,`total_mileage_km=12345.6` - 第二条 808 帧:`device_time=2026-07-01T23:09:36+08:00`,`total_mileage_km=0` - 两条帧使用同一 VIN:`LKLG7C4E3NA774736` Redis merged 快照确认:第二条 0 里程帧没有覆盖已有有效总里程。 ```text event_time_ms=1782918576000 source_endpoint=172.20.0.1:40946 speed_kmh=0 longitude=119.557997 latitude=29.049092 total_mileage_km=12345.6 field_times_ms.total_mileage_km=1782918575000 ``` MySQL `vehicle_daily_metric` 确认:0 里程采样未进入统计,sample_count 只因有效总里程采样增加一次。 ```text LKLG7C4E3NA774736 | JT808 | 2026-07-01 | daily_mileage_km | 0.000 | sample_count=13 | first=12345.600 | latest=12345.600 LKLG7C4E3NA774736 | JT808 | 2026-07-01 | daily_total_mileage_km | 12345.600 | sample_count=13 | first=12345.600 | latest=12345.600 ``` 说明:生产 TDengine 中已经存在大量 808 非零 `total_mileage_km` 采样,但许多 phone 尚未映射到 VIN,因此 MySQL 按 VIN 的统计只会覆盖已解析 VIN 的车辆。下一步应继续补齐 `vehicle_identity_binding`,让更多 808 车辆进入统计。