package telemetry import ( "encoding/json" "strconv" "strings" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) type MileageFieldMapping struct { Key string Scale float64 } // MileageFieldMappings is the single protocol-to-unit contract used by every // projection that exposes or calculates total mileage. func MileageFieldMappings(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_km", Scale: 1}, {Key: "yutong_mqtt.root.data.total_mileage_km", Scale: 1}, {Key: "yutong_mqtt.data.total_mileage", Scale: 0.001}, {Key: "yutong_mqtt.root.data.total_mileage", Scale: 0.001}, } default: return nil } } func TotalMileageKM(protocol envelope.Protocol, fields map[string]any) (float64, bool) { for _, mapping := range MileageFieldMappings(protocol) { value, ok := Number(fields, mapping.Key) if !ok { continue } return value * mapping.Scale, true } return 0, false } func Number(fields map[string]any, key string) (float64, bool) { if len(fields) == 0 { return 0, false } value, ok := fields[key] if !ok || value == nil { return 0, false } switch typed := value.(type) { case float64: return typed, true case float32: return float64(typed), true case int: return float64(typed), true case int8: return float64(typed), true case int16: return float64(typed), true case int32: return float64(typed), true case int64: return float64(typed), true case uint: return float64(typed), true case uint8: return float64(typed), true case uint16: return float64(typed), true case uint32: return float64(typed), true case uint64: return float64(typed), true case json.Number: parsed, err := typed.Float64() return parsed, err == nil case string: parsed, err := strconv.ParseFloat(strings.TrimSpace(typed), 64) return parsed, err == nil default: return 0, false } }