// ================================================= // VEHICLE FULL / PAUSED - NUMERIC FILTER DECISION // IMPORTANT: // - uniquement en session active // - uniquement si la source est réellement livrable // - sans utiliser le PV comme critère // ================================================= if ( vehicleDemandFilter.evcsVehicleNoDemandSemantic && vehicleDemandFilter.sourceDeliverableForNoDemand ) { setOff({ stateName: 'vehicle_full_or_paused', decisionName: 'stop_vehicle_full_or_paused', reasonText: 'Véhicule branché mais ne demande plus réellement de puissance', dynamic: false, mustStopFlag: false, blockReason: 'none', blockedUntilStamp: now.stamp, }); reason.push(`DeliveryRatioEma=${vehicleDemandFilter.deliveryRatioEma}`); reason.push(`NoiseEma=${vehicleDemandFilter.deliveryNoiseEma}`); reason.push(`NoDemandScore=${vehicleDemandFilter.noDemandScoreEma}`); reason.push(`ScoreOn=${vehicleDemandFilter.noDemandScoreOn}`); reason.push(`ScoreOff=${vehicleDemandFilter.noDemandScoreOff}`); if (vehicleDemandFilter.evcsNoDemandSinceMinutes !== null) { reason.push(`NoDemand depuis ${round1(vehicleDemandFilter.evcsNoDemandSinceMinutes)} min`); } } else if (activeSourceMode === 'blocked') { if (lifecycle.minRunElapsed) { setOff({ stateName: 'stop_source_lost', decisionName: 'stop', reasonText: 'Source autorisée perdue', dynamic: false, mustStopFlag: true, blockReason: context.profile === 'outside_window' ? 'outside_window' : 'source_unavailable', blockedUntilStamp: context.profile === 'outside_window' && Number.isFinite(locks.nextAllowedWindowStartMs) ? formatParisStampFromMs(locks.nextAllowedWindowStartMs) : formatParisStampFromMs(nowMs + params.minStopMinutes * 60000), }); } else { setOn({ stateName: 'hold_min_run_source_lost', decisionName: 'hold', currentA: Math.min(derived.currentReferenceA, derived.physicalCapA), reasonText: "Source perdue mais maintien jusqu'au mini ON", dynamic: false, mustStopFlag: false, }); } } else if ( (activeSourceMode === 'grid_hc_allowed' || activeSourceMode === 'grid_forced') && !parsed.gridAvailable ) { if (lifecycle.minRunElapsed) { setOff({ stateName: context.profile === 'night_hc' ? 'night_wait_grid_takeover_runtime' : 'wait_grid_source_runtime', decisionName: 'hold', reasonText: context.profile === 'night_hc' ? 'Relais réseau demandé pendant la session nuit EVCS' : 'Source réseau demandée mais pas encore disponible', dynamic: false, mustStopFlag: false, blockReason: context.profile === 'night_hc' ? 'grid_hc_unavailable' : 'grid_unavailable', blockedUntilStamp: formatParisStampFromMs(nowMs + 10 * 60000), }); } else { setOn({ stateName: 'hold_min_run_wait_grid_takeover', decisionName: 'hold', currentA: Math.min(derived.currentReferenceA, derived.physicalCapA), reasonText: "Maintien jusqu'au mini ON en attente du réseau", dynamic: false, mustStopFlag: false, }); } } else if (!holdConditionsOk) { if (lifecycle.minRunElapsed) { setOff({ stateName: 'stop_conditions_lost', decisionName: 'stop', reasonText: 'Conditions de maintien perdues', dynamic: false, mustStopFlag: true, blockReason: 'hold_conditions_lost', blockedUntilStamp: formatParisStampFromMs(nowMs + 10 * 60000), }); } else { setOn({ stateName: 'hold_min_run', decisionName: 'hold', currentA: Math.min(derived.currentReferenceA, derived.physicalCapA), reasonText: "Maintien jusqu'au temps mini ON", dynamic: true, mustStopFlag: false, }); } } else if ((derived.currentReferenceA > derived.physicalCapA) && lifecycle.currentCooldownElapsed) { setOn({ stateName: 'charging_step_down_system_cap', decisionName: 'step_down', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - Math.max(params.currentStepDownA, 2), derived.physicalCapA ) ), reasonText: 'Réduction pour respecter la capacité système', dynamic: true, mustStopFlag: false, }); } else if (conditions.shouldStepDownOverload) { setOn({ stateName: 'charging_step_down_overload', decisionName: 'step_down_fast', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - Math.max(batteryTune.global.overloadStepDownA, params.currentStepDownA), derived.physicalCapA ) ), reasonText: 'Baisse rapide: surcharge convertisseur', dynamic: true, mustStopFlag: false, }); } else if (conditions.shouldStepDownVebusPower) { setOn({ stateName: 'charging_step_down_vebus_power', decisionName: 'step_down_fast', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - Math.max(params.currentStepDownA + 1, 3), derived.physicalCapA ) ), reasonText: 'Baisse rapide: puissance VE.Bus élevée', dynamic: true, mustStopFlag: false, }); } else if (conditions.shouldStepDownVebusVoltage) { setOn({ stateName: 'charging_step_down_dc_voltage', decisionName: 'step_down_fast', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - Math.max(params.currentStepDownA + 1, 3), derived.physicalCapA ) ), reasonText: 'Baisse rapide: tension DC basse', dynamic: true, mustStopFlag: false, }); } else if ( activeSourceMode === 'battery_assist_night' && conditions.batteryAssistNightShouldStepDown ) { setOn({ stateName: 'charging_step_down_battery_assist_night', decisionName: 'step_down', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - params.currentStepDownA, derived.physicalCapA ) ), reasonText: nightEvcs?.batteryRecoveryUrgent ? 'Réduction: approche départ, préparation takeover réseau' : 'Réduction: assistance batterie nuit devient trop coûteuse', dynamic: true, mustStopFlag: false, }); } else if ( (activeSourceMode === 'grid_hc_allowed' || activeSourceMode === 'grid_forced') && conditions.gridShouldStepDownBattery ) { setOn({ stateName: activeSourceMode === 'grid_forced' ? 'charging_step_down_grid_forced_guard' : 'charging_step_down_grid_guard', decisionName: 'step_down', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - params.currentStepDownA, derived.physicalCapA ) ), reasonText: activeSourceMode === 'grid_forced' ? 'Réduction réseau forcé: la batterie commence à participer' : 'Réduction réseau: la batterie commence à participer', dynamic: true, mustStopFlag: false, }); } else if ( (activeSourceMode === 'solar_only' || activeSourceMode === 'solar_potential') && conditions.solarShouldStepDownCloudDebt ) { setOn({ stateName: 'charging_step_down_cloud_debt', decisionName: 'step_down', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - Math.max(params.currentStepDownA, 2), derived.physicalCapA ) ), reasonText: 'Réduction: dette nuage trop élevée', dynamic: true, mustStopFlag: false, }); } else if ( (activeSourceMode === 'solar_only' || activeSourceMode === 'solar_potential') && conditions.solarShouldStepDownBattery ) { setOn({ stateName: 'charging_step_down_battery', decisionName: 'step_down', currentA: Math.max( params.currentMinA, Math.min( derived.currentReferenceA - params.currentStepDownA, derived.physicalCapA ) ), reasonText: 'Réduction: batterie trop sollicitée', dynamic: true, mustStopFlag: false, }); } else if ( (activeSourceMode === 'grid_hc_allowed' || activeSourceMode === 'grid_forced') && conditions.gridShouldStepUp ) { setOn({ stateName: activeSourceMode === 'grid_forced' ? ( nightEvcs?.batteryRecoveryUrgent ? 'night_departure_deadline_grid_forced' : 'charging_step_up_grid_forced' ) : 'charging_step_up_grid_hc', decisionName: 'step_up', currentA: clamp( derived.currentReferenceA + params.currentStepUpA, params.currentMinA, derived.physicalCapA ), reasonText: activeSourceMode === 'grid_forced' ? ( nightEvcs?.batteryRecoveryUrgent ? 'Hausse prudente: priorité départ, réseau forcé' : 'Hausse prudente du courant en charge réseau forcée' ) : 'Hausse prudente du courant en HC réseau', dynamic: true, mustStopFlag: false, }); } else if ( activeSourceMode === 'battery_assist_night' && conditions.batteryAssistNightShouldStepUp ) { setOn({ stateName: 'charging_step_up_battery_assist_night', decisionName: 'step_up', currentA: clamp( derived.currentReferenceA + params.currentStepUpA, params.currentMinA, derived.physicalCapA ), reasonText: 'Hausse prudente du courant en assistance batterie nuit', dynamic: true, mustStopFlag: false, }); } else if ( (activeSourceMode === 'solar_only' || activeSourceMode === 'solar_potential') && conditions.solarShouldStepUp ) { setOn({ stateName: conditions.probeSuccessNow ? 'probe_success_step_up' : (modes.batteryPriorityMode ? 'charging_step_up_battery' : 'charging_step_up_forecast'), decisionName: 'step_up', currentA: clamp( derived.currentReferenceA + params.currentStepUpA, params.currentMinA, derived.physicalCapA ), reasonText: 'Hausse prudente du courant', dynamic: true, mustStopFlag: false, }); } else { setOn({ stateName: conditions.probeSuccessNow ? 'probe_success_hold' : activeSourceMode === 'battery_assist_night' ? 'charging_hold_battery_assist_night' : (activeSourceMode === 'grid_hc_allowed' || activeSourceMode === 'grid_forced') ? 'charging_hold_grid' : cloud.cloudDebtActive ? 'charging_hold_cloud_tolerance' : 'charging_hold', decisionName: 'hold', currentA: Math.min(derived.currentReferenceA, derived.physicalCapA), reasonText: activeSourceMode === 'battery_assist_night' ? 'Maintien du courant en assistance batterie nuit' : 'Maintien du courant', dynamic: true, mustStopFlag: false, }); } } }