← Retour à l'index

4. Carburant

Du capteur au reçu : automate 3 états (normal / draining / refilling), filtre median, détection de drain anormal, ravitaillement hors station, reconciliation reçu/capteur, bilan mission en FCFA. 22 cas d'usage.

3États machine
6Types d'événements
2Sources capteur
3Reconciliation
A. Automate d'état carburant (7 cas) B. Cas limites (4 cas) C. Événements carburant (6 cas) D. Reconciliation (3 cas) E. Métriques trajet (2 cas) SVG 1. Automate 3 états SVG 2. Courbe de niveau journaliere SVG 3. Flow de reconciliation SVG 4. Bilan mission FCFA SVG 5. Arbre isAbnormalDrain

Automate d'état carburant

Trois états, détection FLS uniquement. CAN utilise un compteur cumulatif aux bornes de trajet — pas de machine à états.

Diagramme 1 NORMAL état par défaut DRAINING perte en cours REFILLING remplissage en cours Niveau chute > min_drain_liters (apres filtre median) Niveau stabilisé 300s sans chute Niveau monte > min_filling_liters (apres filtre median) Niveau stabilisé 300s sans hausse mutuellement exclusifs Join: fuelDrainJoinSeconds 2 drains < 5 min = 1 seul événement Join: fuelFillingJoinSeconds 2 remplissages < 5 min = 1 seul

Automate 3 états du module carburant. Draining et refilling sont mutuellement exclusifs. Join intervals fusionnent les oscillations capteur.

A. Automate d'état carburant — 7 cas

Décisions fondatrices : D-fuel-state-machine (3 états dans le moteur pur), D-fuel-sensor-priority (FLS pour détection, CAN pour consommation), D-fuel-vehicle-config (seuils par véhicule, pas par tenant).

1. Detection de perte

normal → draining

Le niveau FLS median chute au-delà de min_drain_liters (défaut 5L). INSERT fuel_events (type=drain, state=active, initial_level).

Réservoir 600L : chute de 85% a 76% = -54L → transition immediate

2. Fin de perte

draining → normal

Le niveau se stabilise pendant fuelDrainJoinSeconds (300s). UPDATE fuel_events (state=closed, final_level, volume, duration).

Plus de chute depuis 5 min → clôture du drain

3. Detection de remplissage

normal → refilling

Le niveau FLS median monte au-delà de min_filling_liters (défaut 5L). INSERT fuel_events (type=filling, state=active).

Niveau passe de 42% a 58% = +96L → remplissage détecté

4. Fin de remplissage

refilling → normal

Niveau stable pendant fuelFillingJoinSeconds (300s). UPDATE fuel_events (state=closed, final_level, volume).

Plein termine a 95% → événement clôture

5. Fusion d'intervalles (join)

join

2 événements du même type en < 5 min → fusionnés en un seul. Drain+drain = 1 événement. Drain+remplissage = 2 événements séparés. Évite les duplications par oscillation capteur.

Pompe s'arrête 2 min puis reprend → un seul remplissage

6. Exclusion mutuelle

invariant

Draining et refilling ne peuvent pas etre actifs simultanement. Un signal de remplissage pendant un drain actif est ignore jusqu'a la clôture du drain.

Glitch capteur monte pendant un drain → ignore

7. Filtre median

pre-traitement

Fenêtre de 15 messages (7.5 min a 30s/msg). fuelFilterLevel 0=3 msgs, 3 (défaut)=15 msgs. Lisse les oscillations de carburant en mouvement. Applique au FLS uniquement, pas au CAN (l'ECU pre-filtre).

Route de montagne → niveau oscille ±3% → median elimine le bruit

B. Cas limites — 4 cas

8. Pas de capteur FLS

skip

Si fuel_level_percent est NULL sur toutes les positions du batch, l'étape 8b (detectFuelState) est entièrement sautee. Aucun événement. fuelState reste normal.

Camion sans sonde → seuls les reçus chauffeur sont disponibles

9. Suppression démarrage a froid

cold start

Si fuelLevelHistory contient moins de lectures que la fenêtre du filtre median (fuelFilterLevel + 1), toutes les transitions sont supprimees. Le moteur collecte les lectures sans evaluer les seuils.

Véhicule équipé depuis 3 min → 6 lectures < 15 → pas de transition

10. Remplacement capteur

reset

Saut soudain >30 points de pourcentage entre lectures consécutives (sans drain/remplissage en cours) → reset de fuelLevelHistory + mode démarrage a froid. Empêche les fausses détections par recalibration.

Ancien capteur 45% → nouveau capteur 82% → reset + 15 lectures avant evaluation

11. Rate limit defaillance capteur

sécurité

>20 événements/heure → alerte sensor_fault_suspected (warning). Les événements carburant sont suspendus jusqu'a ce que le taux retombe. Protege contre un capteur defaillant qui inonde le systeme.

Sonde cassee oscille 40 fois/heure → suspension + avertissement fleet owner

Courbe de niveau journaliere

Simulation d'une journee type : consommation normale, drain suspect, ravitaillement, conduite avec niveau bas.

Diagramme 2 100% 80% 60% 40% 20% 0% Niveau FLS 15% 06:00 08:00 10:00 12:00 14:00 16:00 18:00 -54L en 25 min = 40 500 FCFA +95L ravitaillement Station Total Garoua low_fuel_driving Normal Drain Remplissage Seuil 15%

Journee type : consommation normale → drain suspect (54L, 40 500 FCFA) → plat → ravitaillement (+95L) → consommation → seuil bas franchi

C. Événements carburant — 6 cas

Architecture : Le moteur (étape 8b) persiste les faits dans fuel_events. La couche de publication crée les jugements dans vehicle_events, lies via fuel_event_id + related_event_id. Fait (fuel_events) → Jugement (vehicle_events).

12. fuel_drain_anomaly

CRITICAL

Drain rapide (<30 min) + hors station-service enregistrée + volume > seuil. Ne s'auto-résout jamais — investigation humaine obligatoire.

54L en 25 min, pas à une station → 40 500 FCFA de perte suspecte

13. off_station_refueling

WARNING

Remplissage détecté hors d'une station-service enregistrée. Événement dérivé de refueling_détectéd via related_event_id. Ne s'auto-résout jamais.

+80L détecté près de Meiganga, pas de station connue → à vérifier

14. refueling_détectéd

INFO

Événement de base à chaque clôture de remplissage. Status = closed immédiatement. Pas de cycle de vie. Alimente le journal véhicule et les métriques trajet.

+95L a Station Total Garoua, 14:22

15. fuel_drain_détectéd

INFO

Événement de base à chaque clôture de drain. Alimente le journal véhicule. Si les critères isAbnormalDrain sont remplis, un fuel_drain_anomaly dérivé est créé.

-12L en 45 min pres d'une station → info seulement (maintenance possible)

16. low_fuel_driving

WARNING

Niveau <15% ET vitesse > 0. Auto-résolution quand le niveau remonte. Évaluateur position par position, pas d'entité fuel_events.

Niveau 12%, vitesse 65 km/h → alerte conducteur + fleet owner

17. low_fuel

INFO

Niveau <15% ET véhicule à l'arrêt. Status closed immédiatement. Un seul par véhicule par jour calendaire (idempotency key: low_fuel:{vehicle_id}:{calendar_date}).

Niveau 8% stationne pour la nuit → info pour planification du lendemain

ALERTE CRITIQUE #K47 — Perte carburant anormale
Véhicule : K12 (AB-1234-CM)
Perte : 54L en 25 min
Lieu : Route N1, km 342 (hors station)
Valeur : 40 500 FCFA
Heure : 08:35

Répondre #V47 pour prendre en charge

Arbre de décision isAbnormalDrain

Diagramme 3 Drain détecté FuelDrainEnded A une station- service ? OUI PAS D'ANOMALIE NON Durée < 30 min ? NON PAS D'ANOMALIE maintenance ou transfert légitime OUI Volume > minDrainLiters ? NON PAS D'ANOMALIE OUI ANO- MALIE isAbnormalDrain : 3 conditions cumulatives !is_at_fuel_station AND duration_seconds < 1800 AND volume_liters > minDrainLiters Un drain long (>30 min) ou à une station n'est pas signalé — maintenance ou transfert légitime possible.

Arbre de décision isAbnormalDrain : les 3 conditions sont cumulatives — hors station + rapide + volume significatif

D. Reconciliation — 3 cas

Flow de reconciliation capteur/reçu

Diagramme 4 Reconciliation automatique : capteur + reçu chauffeur Capteur FLS Automatique, 30s fuel_events +95L détecté a 14:22 Reçu chauffeur Manuel, driver app fuel_transactions 100L, 75 000 FCFA, Station Total KORIDO Engine Fenêtre : ±30 min Comparaison Capteur : 95L | Reçu : 100L Écart : 5L = 3 750 FCFA discrepancy_liters = transaction.volume - fuel_event.volume Écart < seuil OK, pas d'alerte Écart > seuil fuel_gauge_discrepancy Aucun match capteur matched_fuel_event_id = NULL Signalé pour vérification

Deux flux convergent : le capteur automatique (fuel_events) et le reçu chauffeur (fuel_transactions). Fenêtre de ±30 min pour le matching.

18. Match reçu / capteur

RECONCILIATION

Le chauffeur soumet un reçu (POST /mobile/fuel-receipt). Le systeme trouve un fuel_event de type filling dans la fenêtre ±30 min. matched_fuel_event_id est positionne, discrepancy_liters calcule.

Reçu 100L a 14:30 → filling détecté a 14:22 (95L) → écart 5L

19. fuel_gauge_discrepancy

WARNING

Écart déclaré ≠ mesuré au-delà du seuil → événement warning. Ne s'auto-résout JAMAIS — vérification humaine requise. L'écart est exprimé en FCFA pour le fleet owner.

Déclaré 100L, mesuré 95L → écart 5L = 3 750 FCFA

20. Reçu sans événement capteur

NON MATCH

Aucun fuel_event dans ±30 min → stocké avec matched_fuel_event_id = NULL, signalé pour vérification. Jamais de rejet des données chauffeur. Si tankCapacityLiters est NULL (pas de capteur), pas de vérification d'écart.

"Reçu soumis (80L, Station Garoua) mais aucun ravitaillement détecté par le capteur — à vérifier."

Alerte #K52 — Écart carburant
Véhicule : K12 (AB-1234-CM)
Reçu chauffeur : 100L (75 000 FCFA)
Capteur : 95L
Écart : 5L = 3 750 FCFA
Station : Total Garoua

Répondre #V52 pour prendre en charge

E. Métriques trajet — 2 cas

Decision D-fuel-sensor-priority : FLS pour les événements (remplissage/perte). CAN pour la consommation trajet (compteur cumulatif ECU, précision 1-5%). Les deux se completent quand disponibles.

21. Double source FLS / CAN

METRIQUES

Au trajet : fuel_start_pct / fuel_end_pct (FLS) + can_fuel_consumed (CAN). Efficacite L/100km calculee depuis CAN si disponible, sinon delta FLS × tank_capacity_liters.

Trip Garoua→Maroua : CAN 45L, FLS 43.2L, efficacité 28.5 L/100km

22. Garde reset compteur CAN

GARDE

Si le delta CAN est négatif à la clôture du trajet (reset ECU ou débordement compteur) → can_fuel_consumed = null, fallback sur FLS : (fuel_start_pct - fuel_end_pct) * tank_capacity_liters. Jamais traiter un delta négatif comme zéro.

CAN actuel 12 450ml, CAN départ 13 200ml → delta -750ml → null, fallback FLS

Colonnes carburant sur la table trips (D-trip-fuel-metrics)

ColonneTypeSourceDescription
fuel_start_pctNUMERIC(5,2)FLSNiveau % au départ du trajet
fuel_end_pctNUMERIC(5,2)FLSNiveau % à la fin du trajet
fuel_consumed_litersNUMERIC(7,2)CAN ou FLSCAN delta prioritaire, sinon FLS delta × capacite
fuel_efficiency_l100kmNUMERIC(5,2)calculeconsumed / (distance_m / 100000)
can_fuel_consumedNUMERIC(7,2)CANDelta compteur cumulatif ECU (null si reset)
idle_fuel_litersNUMERIC(7,2)CANConsommation au ralenti (si disponible)
fillings_countINTEGERengineNombre de remplissages pendant le trajet
fillings_volume_litersNUMERIC(7,2)engineVolume total des remplissages pendant le trajet

Bilan mission FCFA

Exemple : mission Douala → N'Djamena, camion K12, réservoir 600L.

Diagramme 5 Bilan carburant — Mission Douala → N'Djamena K12 (AB-1234-CM) — 1 950 km — 34h — Réservoir 600L POSTE DÉCLARÉ (reçus) MESURÉ (capteur) ECART IMPACT FCFA SEVERITE Consommation route 487L (CAN) Ravitaillement #1 (CM) 200L 192L 8L 6 000 FCFA warning Ravitaillement #2 (CM) 200L 184L 16L 12 000 FCFA warning Ravitaillement #3 (TD) 120L 86L 34L 25 500 FCFA warning Drain anormal (km 342) 54L 54L 40 500 FCFA CRITICAL Total écarts reçus 520L 462L 58L 43 500 FCFA warning PERTES TOTALES MISSION 112L 84 000 FCFA dont 40 500 vol Prix CM : 750 FCFA/L | Prix TD : 750 FCFA/L | Efficacite mission : 25.0 L/100km (CAN) | 3 remplissages, 1 drain anormal country_code dérivé du dernier border_crossing. Écart = transaction.volume - fuel_event.volume. Anomalie = isAbnormalDrain(<30min, hors station, >seuil).

Bilan FCFA d'une mission Douala → N'Djamena : 58L d'écart reçus (43 500 FCFA) + 54L drain anormal (40 500 FCFA) = 84 000 FCFA de pertes totales

Schémas de données

Table fuel_events (D-fuel-entity)

Entité de données calculées — même niveau que trips, stops, gaps. Les faits (drain, filling) sont ici. Les jugements (fuel_drain_anomaly, off_station_refueling) sont dans vehicle_events.

ColonneTypeDescription
idUUID PKgen_random_uuid()
tenant_idUUID NOT NULLIsolation multi-tenant, RLS
vehicle_idUUID NOT NULLFK vehicles
event_typeTEXT'filling' | 'drain'
stateTEXT'active' | 'closed'
started_atTIMESTAMPTZDébut de l'événement
ended_atTIMESTAMPTZFin (null si actif)
duration_secondsINTEGERDurée totale
initial_level_pctNUMERIC(5,2)Niveau FLS au debut
final_level_pctNUMERIC(5,2)Niveau FLS à la fin
volume_litersNUMERIC(7,2)Delta × tank_capacity (null si capacite inconnue)
positionGEOGRAPHY(Point)Localisation au debut
is_at_fuel_stationBOOLEANProximité d'une station enregistrée
detection_sourceTEXT'fls' | 'can' | 'both'
confidenceTEXT'high' | 'medium' | 'low'
Index clé : UNIQUE (vehicle_id, event_type) WHERE state = 'active' — un seul drain actif et un seul remplissage actif par véhicule à tout moment.

Table fuel_transactions (D-fuel-transactions)

Reçus chauffeur pour la reconciliation financiere et le suivi par pays.

ColonneTypeDescription
idUUID PKgen_random_uuid()
tenant_idUUID NOT NULLRLS
vehicle_idUUID NOT NULLFK vehicles
driver_idUUIDFK users (nullable)
transaction_atTIMESTAMPTZHorodatage du reçu
volume_litersNUMERIC(7,2)Volume déclaré par le chauffeur
cost_fcfaNUMERIC(10,2)Cout total en FCFA
station_nameTEXTNom de la station
receipt_photo_urlTEXTPhoto du reçu (R2)
matched_fuel_event_idUUIDFK fuel_events (null si pas de match)
discrepancy_litersNUMERIC(7,2)transaction.volume - fuel_event.volume
country_codeTEXTCM ou TD (depuis border_crossing)
fuel_price_per_literNUMERIC(7,2)Prix réglementé au moment de l'achat

Configuration par véhicule (D-fuel-vehicle-config)

Pourquoi par véhicule ? Les flottes ont des camions avec des réservoirs de tailles différentes (300L vs 600L). Un drain de 20L représente 6.7% sur un 300L mais seulement 3.3% sur un 600L. Les seuils en litres absolus sont liés à la géométrie du capteur et du réservoir.
5Lmin_drain_liters
(défaut)
5Lmin_filling_liters
(défaut)
300sjoin seconds
(drain + filling)
3filter level
(= 15 msg window)
ParamètreNiveauDéfautDescription
tank_capacity_litersvéhiculenullCapacité réservoir. Si null, volume_liters = null sur fuel_events
min_drain_litersvéhicule5LSeuil de détection de perte (en litres absolus)
min_filling_litersvéhicule5LSeuil de détection de remplissage
fuel_filter_levelvéhicule30=3 msg, 1=5 msg, 2=9 msg, 3=15 msg (fenêtre median)
fuel_drain_join_secondsvéhicule300Fenêtre de fusion pour drains consécutifs
fuel_filling_join_secondsvéhicule300Fenêtre de fusion pour remplissages consécutifs
fuel_drain_threshold_percenttenantSeuil d'alerte (tenant_event_config)
low_fuel_threshold_percenttenant15%Seuil low_fuel (tenant_event_config)

Matrice des 6 types d'événements carburant

Type Sévérité Auto-resolve Table source Déclencheur
fuel_drain_anomaly CRITICAL Jamais fuel_events (drain) isAbnormalDrain = true
off_station_refueling WARNING Jamais fuel_events (filling) !is_at_fuel_station
fuel_gauge_discrepancy WARNING Jamais fuel_transactions discrepancy > seuil
low_fuel_driving WARNING Oui (niveau remonte) position stream fuel < 15% + speed > 0
low_fuel INFO N/A (clos immédiatement) position stream fuel < 15% + stationnaire
refueling_détectéd INFO N/A (clos immédiatement) fuel_events (filling) FuelFillingEnded
fuel_drain_détectéd INFO N/A (clos immédiatement) fuel_events (drain) FuelDrainEnded
Règle clé : fuel_drain_anomaly, off_station_refueling et fuel_gauge_discrepancy ne s'auto-résolvent JAMAIS. Chaque incident de carburant nécessite une investigation humaine. Le propriétaire de flotte décide si c'est un vol, une erreur capteur ou un transfert légitime.

Décisions clés

D-fuel-entityfuel_events est une entité de données calculées (même niveau que trips, stops, gaps). Les jugements (anomalies, écarts) vivent dans vehicle_events et sont dérivés via related_event_id.
D-fuel-state-machine — 3 états dans le moteur pur. Join intervals de 300s pour fusionner les oscillations capteur. CAN n'a pas de machine à états (compteur cumulatif).
D-fuel-sensor-priority — FLS pour les événements (mesure le contenu du réservoir). CAN pour la consommation trajet (compteur ECU, précision 1-5%). detection_source sur fuel_events enregistre la contribution de chaque capteur.
D-fuel-vehicle-config — Seuils en litres absolus par véhicule (pas par tenant). tank_capacity_liters convertit les % en litres. Nullable si inconnu — le moteur travaille alors en pourcentages uniquement.
D-fuel-transactions — Reconciliation dans une fenêtre de ±30 min. country_code depuis le dernier border_crossing. Écart en FCFA pour le fleet owner. Les reçus ne sont jamais rejetes.
D-trip-fuel-metrics — 8 colonnes carburant sur trips. Priorité : CAN > FLS > taux configuré. Le garde CAN reset (delta négatif → null) empêche les fausses métriques.

22 cas d'usage documentes. Source : state-machines.md §3.7, pipeline.md step 8b, async-ops.md §10, schemas.md §§2.8-2.9, décisions.md D-fuel-*
← Retour à l'index