Files
EVCS/decoupesurplus/V2/DECISION/decisionP3.js

430 lines
14 KiB
JavaScript

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,
});
}
}
}
// =====================================================
// START BRANCH
// =====================================================
else {
const startConditionsOk =
(context.sourceMode === 'grid_hc_allowed' || context.sourceMode === 'grid_forced')
? conditions.gridStartOk
: (context.sourceMode === 'solar_only' || context.sourceMode === 'solar_potential')
? conditions.solarStartOk
: context.sourceMode === 'hybrid_probe'
? conditions.probeStartOk
: context.sourceMode === 'battery_assist_night'
? conditions.batteryAssistNightStartOk
: false;
if (!derived.canRunAtMinCurrent) {
setOff({
stateName: 'blocked_by_capacity_min_current',
decisionName: 'hold',
reasonText: 'Capacité insuffisante pour courant mini',
dynamic: false,
mustStopFlag: false,
blockReason: 'system_capacity_stop',
blockedUntilStamp: formatParisStampFromMs(nowMs + 10 * 60000),
});
}
else if (context.sourceMode === 'blocked') {
setOff({
stateName:
context.profile === 'outside_window'
? 'blocked_outside_window'
: (context.profile === 'night_hc'
? 'night_no_grid_hc_lock'
: 'idle_wait_source'),
decisionName: 'hold',
reasonText:
context.profile === 'outside_window'
? 'Hors fenêtre préférée sans source suffisante'
: context.profile === 'night_hc'
? 'Nuit sans autorisation HC réseau'
: 'Source non autorisée ou insuffisante',
dynamic: false,
mustStopFlag: false,
blockReason:
context.profile === 'outside_window'
? 'outside_window'
: context.profile === 'night_hc'
? 'grid_hc_unavailable'
: 'source_unavailable',
blockedUntilStamp:
Number.isFinite(locks.nextAllowedWindowStartMs)
? formatParisStampFromMs(locks.nextAllowedWindowStartMs)
: formatParisStampFromMs(nowMs + 15 * 60000),
});
}
else if (
(context.sourceMode === 'grid_hc_allowed' || context.sourceMode === 'grid_forced') &&
!parsed.gridAvailable
) {
setOff({
stateName:
context.profile === 'night_hc'
? 'night_wait_grid_takeover'
: 'wait_grid_source',
decisionName: 'hold',
reasonText:
context.profile === 'night_hc'
? (
nightEvcs?.batteryRecoveryUrgent
? 'Réseau demandé: urgence recharge batterie avant départ'
: 'Prise de relais réseau demandée pour 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 if (locks.startBlockedByReason) {
setOff({
stateName: 'wait_reason_lock',
decisionName: 'hold',
reasonText: `Blocage anti-cliquetis: ${history.lastBlockReason}`,
dynamic: false,
mustStopFlag: false,
blockReason: history.lastBlockReason,
blockedUntilStamp: history.blockedUntilAt || now.stamp,
});
}
else if (!startConditionsOk) {
setOff({
stateName: 'idle_wait_conditions',
decisionName: 'hold',
reasonText:
context.sourceMode === 'battery_assist_night'
? 'Conditions de démarrage nuit batterie non réunies'
: 'Conditions de démarrage non réunies',
dynamic: false,
mustStopFlag: false,
blockReason: 'none',
blockedUntilStamp: now.stamp,
});
}
else if (!lifecycle.minStopElapsed) {
setOff({
stateName: 'wait_min_stop',
decisionName: 'hold',
reasonText: 'Temps mini OFF non écoulé',
dynamic: false,
mustStopFlag: false,
blockReason: 'none',
blockedUntilStamp: now.stamp,
});
}
else {
const startCurrentA =
context.sourceMode === 'hybrid_probe'
? clamp(params.probeCurrentA, params.currentMinA, derived.physicalCapA)
: params.currentMinA;
setOn({
stateName:
context.sourceMode === 'grid_forced' ? (
nightEvcs?.batteryRecoveryUrgent
? 'night_departure_deadline_grid_forced'
: 'start_grid_forced_min_current'
) :
context.sourceMode === 'grid_hc_allowed' ? 'start_grid_hc_min_current' :
context.sourceMode === 'battery_assist_night' ? 'start_battery_assist_night_min_current' :
context.sourceMode === 'hybrid_probe' ? 'probe_start_6a' :
context.sourceMode === 'solar_potential' ? 'start_forecast_solar_potential' :
modes.batteryFullHarvestMode ? 'start_min_current_battery_full_harvest' :
modes.forecastCommitMode ? 'start_min_current_forecast_commit' :
'start_min_current',
decisionName: 'start',
currentA: startCurrentA,
reasonText:
context.sourceMode === 'grid_forced'
? (
nightEvcs?.batteryRecoveryUrgent
? 'Démarrage réseau forcé: priorité départ / recharge batterie'
: 'Démarrage en charge réseau forcée'
)
: context.sourceMode === 'grid_hc_allowed'
? 'Démarrage en fenêtre HC réseau'
: context.sourceMode === 'battery_assist_night'
? 'Démarrage nuit en assistance batterie contrôlée'
: context.sourceMode === 'hybrid_probe'
? 'Démarrage en mode sonde'
: context.sourceMode === 'solar_potential'
? 'Démarrage par potentiel solaire confirmé'
: modes.batteryFullHarvestMode
? 'Démarrage harvest solaire'
: modes.forecastCommitMode
? 'Démarrage forecast commit'
: 'Démarrage prudent à courant mini',
dynamic: true,
mustStopFlag: false,
});
nextBlockReason = 'none';
nextBlockedUntilStamp = now.stamp;
setProbeStartedAt = context.sourceMode === 'hybrid_probe';
}
}
// =====================================================
// CONTEXT REASONS
// =====================================================
if (nightEvcs?.nightSessionActive) {
reason.push(
`Nuit EVCS: target=${round1(nightEvcs.batteryMorningTargetPct)}% | trigger réseau=${round1(nightEvcs.gridTakeoverTriggerPct)}%`
);
if (Number.isFinite(nightEvcs.nightSessionStartSocPct)) {
reason.push(
`SOC départ session nuit=${round1(nightEvcs.nightSessionStartSocPct)}% | décharge autorisée=${round1(nightEvcs.maxBatteryDischargePoints)} pts`
);
}
if (nightEvcs.batteryRecoveryUrgent) {
reason.push("Urgence départ: il faut anticiper la reprise réseau");
}
}
// =====================================================
// OUTPUT
// =====================================================
return [{
json: {
...prev,
decision: {
desiredCharging,
desiredPermission,
desiredLatch,
desiredCurrentA,
state,
currentDecision,
dynamicOk,
mustStop,
reason,
nextBlockReason,
nextBlockedUntilStamp,
setCloudDebtStartedAt,
setProbeStartedAt,
setNightSessionStart,
clearNightSessionStart,
notifyWanted,
notifyTitle,
notifyMessage,
}
}
}];