package stats import ( "encoding/json" "fmt" "strconv" "strings" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) const ( gb32960FuelCellEngineWorkMode = 2 yutongFuelCellRunningMode = 4 yutongFuelCellDrivingMode = 11 ) // PureHydrogenModeFromEnvelope returns whether hydrogen power participates in // vehicle propulsion and whether the source value is understood. // // GB32960's standard vehicle running mode is authoritative when present: // mode 1 is pure electric, mode 2 is hybrid, and mode 3 is fuel powered. Mode // 2 is counted as hydrogen-participating mileage for the hydrogen fleet; mode // 1 is excluded. Fuel, abnormal, and invalid values are left unclassified. // Older frames without the standard field fall back to the fuel-cell extension // engine_work_state. Yutong reports the equivalent state through // TRIANGLE_STATE; production frames use 1 for inactive and 4/11 for active. func PureHydrogenModeFromEnvelope(env envelope.FrameEnvelope) (active bool, known bool) { if env.Protocol == envelope.ProtocolGB32960 { if value, exists := env.Fields[envelope.FieldVehicleRunningMode]; exists { mode, ok := integerMode(value) if !ok { return false, false } switch mode { case 1: return false, true case 2: return true, true default: return false, false } } } return FuelCellActiveModeFromEnvelope(env) } // FuelCellActiveModeFromEnvelope reads the fuel-cell subsystem work state. // Hydrogen consumption segmentation uses this signal (and its current-based // fallback) independently from the whole-vehicle running mode used by mileage. func FuelCellActiveModeFromEnvelope(env envelope.FrameEnvelope) (active bool, known bool) { value, exists := env.Fields[envelope.FieldFuelCellWorkMode] if !exists { return false, false } mode, ok := integerMode(value) if !ok { return false, false } switch env.Protocol { case envelope.ProtocolGB32960: switch mode { case gb32960FuelCellEngineWorkMode: return true, true case 0, 1: return false, true default: return false, false } case envelope.ProtocolYutongMQTT: switch mode { case yutongFuelCellRunningMode, yutongFuelCellDrivingMode: return true, true case 1: return false, true default: return false, false } default: return false, false } } // PureHydrogenMileageDelta returns the odometer delta that can be attributed // to hydrogen-participating operation. The current endpoint owns the interval, // matching the daily rule: total mileage minus intervals ending in pure-electric // mode. Missing or invalid current modes remain unclassified. func PureHydrogenMileageDelta( previousKM float64, currentKM float64, previousActive bool, previousKnown bool, currentActive bool, currentKnown bool, ) float64 { delta := currentKM - previousKM if !currentKnown || !currentActive { return 0 } if delta < 0 || delta > maxSelectedDailyMileageKM { return 0 } return delta } func integerMode(value any) (int, bool) { switch typed := value.(type) { case json.Number: parsed, err := strconv.Atoi(typed.String()) return parsed, err == nil case string: parsed, err := strconv.Atoi(strings.TrimSpace(typed)) return parsed, err == nil case int: return typed, true case int8: return int(typed), true case int16: return int(typed), true case int32: return int(typed), true case int64: return int(typed), true case uint: return int(typed), true case uint8: return int(typed), true case uint16: return int(typed), true case uint32: return int(typed), true case uint64: if typed > uint64(^uint(0)>>1) { return 0, false } return int(typed), true case float32: parsed := int(typed) return parsed, float32(parsed) == typed case float64: parsed := int(typed) return parsed, float64(parsed) == typed default: parsed, err := strconv.Atoi(strings.TrimSpace(fmt.Sprint(value))) return parsed, err == nil } }