fix(stats): map protocol mileage fields
This commit is contained in:
@@ -99,7 +99,7 @@ func SamplesFromEnvelope(env envelope.FrameEnvelope, loc *time.Location) ([]Metr
|
||||
if vin == "" {
|
||||
return nil, nil
|
||||
}
|
||||
totalMileage, ok := floatField(env, envelope.FieldTotalMileageKM)
|
||||
totalMileage, ok := totalMileageKMFromEnvelope(env)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -125,6 +125,41 @@ func SamplesFromEnvelope(env envelope.FrameEnvelope, loc *time.Location) ([]Metr
|
||||
}}, nil
|
||||
}
|
||||
|
||||
type mileageFieldMapping struct {
|
||||
key string
|
||||
scale float64
|
||||
}
|
||||
|
||||
func totalMileageKMFromEnvelope(env envelope.FrameEnvelope) (float64, bool) {
|
||||
if value, ok := floatField(env, envelope.FieldTotalMileageKM); ok {
|
||||
return value, true
|
||||
}
|
||||
for _, mapping := range mileageMappingsByProtocol(env.Protocol) {
|
||||
value, ok := floatField(env, mapping.key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
return value * mapping.scale, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func mileageMappingsByProtocol(protocol envelope.Protocol) []mileageFieldMapping {
|
||||
switch protocol {
|
||||
case envelope.ProtocolGB32960:
|
||||
return []mileageFieldMapping{{key: "gb32960.vehicle.total_mileage_km", scale: 1}}
|
||||
case envelope.ProtocolJT808:
|
||||
return []mileageFieldMapping{{key: "jt808.location.total_mileage_km", scale: 1}}
|
||||
case envelope.ProtocolYutongMQTT:
|
||||
return []mileageFieldMapping{
|
||||
{key: "yutong_mqtt.data.total_mileage", scale: 0.001},
|
||||
{key: "yutong_mqtt.root.data.total_mileage", scale: 0.001},
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const upsertDailyMileageSQL = `
|
||||
INSERT INTO vehicle_daily_mileage
|
||||
(vin, stat_date, protocol, daily_mileage_km,
|
||||
|
||||
@@ -40,6 +40,60 @@ func TestSamplesFromEnvelopeDerivesDailyMileageSample(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSamplesFromEnvelopeMapsProtocolMileageFields(t *testing.T) {
|
||||
loc := time.FixedZone("Asia/Shanghai", 8*3600)
|
||||
tests := []struct {
|
||||
name string
|
||||
protocol envelope.Protocol
|
||||
fields map[string]any
|
||||
wantKM float64
|
||||
}{
|
||||
{
|
||||
name: "gb32960",
|
||||
protocol: envelope.ProtocolGB32960,
|
||||
fields: map[string]any{"gb32960.vehicle.total_mileage_km": "38587"},
|
||||
wantKM: 38587,
|
||||
},
|
||||
{
|
||||
name: "jt808",
|
||||
protocol: envelope.ProtocolJT808,
|
||||
fields: map[string]any{"jt808.location.total_mileage_km": "12432.4"},
|
||||
wantKM: 12432.4,
|
||||
},
|
||||
{
|
||||
name: "yutong mqtt meters",
|
||||
protocol: envelope.ProtocolYutongMQTT,
|
||||
fields: map[string]any{"yutong_mqtt.data.total_mileage": "120756000"},
|
||||
wantKM: 120756,
|
||||
},
|
||||
{
|
||||
name: "yutong mqtt root meters",
|
||||
protocol: envelope.ProtocolYutongMQTT,
|
||||
fields: map[string]any{"yutong_mqtt.root.data.total_mileage": float64(22858000)},
|
||||
wantKM: 22858,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
|
||||
Protocol: tt.protocol,
|
||||
VIN: "LNBVIN00000000001",
|
||||
EventTimeMS: time.Date(2026, 7, 1, 8, 30, 0, 0, loc).UnixMilli(),
|
||||
Fields: tt.fields,
|
||||
}, loc)
|
||||
if err != nil {
|
||||
t.Fatalf("SamplesFromEnvelope() error = %v", err)
|
||||
}
|
||||
if len(samples) != 1 {
|
||||
t.Fatalf("sample count = %d", len(samples))
|
||||
}
|
||||
if samples[0].TotalMileageKM != tt.wantKM {
|
||||
t.Fatalf("total mileage = %v, want %v", samples[0].TotalMileageKM, tt.wantKM)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSamplesFromEnvelopeSkipsMissingVIN(t *testing.T) {
|
||||
loc := time.FixedZone("Asia/Shanghai", 8*3600)
|
||||
samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
|
||||
|
||||
Reference in New Issue
Block a user