Files
EVCS/decoupesurplus/DECISION_ENGINE_4.1.js
2026-04-12 05:41:42 +02:00

156 lines
5.0 KiB
JavaScript

// === DECISION INIT ===
let desiredCharging = false;
let desiredPermission = false;
let desiredLatch = false;
let desiredCurrentA = currentMinA;
let state = 'idle';
let currentDecision = 'hold';
let dynamicOk = false;
let mustStop = false;
let reason = [];
let setLastStartAt = false;
let setLastStopAt = false;
let setLastCurrentChangeAt = false;
let setCloudDebtStartedAt = false;
let setProbeStartedAt = false;
let notifyWanted = false;
let notifyTitle = 'Victron EVCS Surplus';
let notifyMessage = '';
let nextBlockReason = 'none';
let nextBlockedUntilStamp = now.stamp;
// === DECISION TREE (WITH STRATEGY LAYERS) ===
if (!controllerEnabled) {
desiredCharging = evcsChargingSwitchOn;
desiredPermission = chargePermissionNow;
desiredLatch = controlLatch;
desiredCurrentA = clamp(currentReferenceA || currentMinA, currentMinA, currentMaxAutoA);
state = 'controller_disabled';
reason.push("Contrôleur désactivé");
}
else if (isStopMode) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'stopped_user';
currentDecision = 'stop_stop_mode';
dynamicOk = false;
mustStop = false;
nextBlockReason = 'manual_stop';
nextBlockedUntilStamp = now.stamp;
reason.push("Arrêt manuel via Stratégie STOP");
}
else if (evcsModeIsAuto) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'mode_auto_conflict';
currentDecision = 'stop_auto_mode_conflict';
dynamicOk = false;
mustStop = true;
reason.push("Borne en mode Auto interne");
}
else if (!evcsDeviceConnected || !evcsVehicleConnected) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'vehicle_not_connected';
currentDecision = 'stop_not_connected';
dynamicOk = false;
mustStop = false;
nextBlockReason = 'vehicle_not_connected';
nextBlockedUntilStamp = now.stamp;
reason.push("Véhicule ou borne non connecté");
}
else if (mustStopHard) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state =
anyVebusOverload ? 'vebus_overload_hard_stop' :
anyVebusLowBattery ? 'vebus_low_battery_hard_stop' :
vebusDcVoltageHardLow ? 'vebus_dc_voltage_hard_stop' :
vebusAcOutHardReached ? 'vebus_ac_hard_stop' :
batteryCurrentHardExceeded ? 'battery_current_hard_stop' :
'hard_stop';
currentDecision = 'stop_hard';
dynamicOk = false;
mustStop = true;
nextBlockReason = 'battery_protection_stop';
nextBlockedUntilStamp = formatParisStampFromMs(nowMs + 15 * 60000);
reason.push("Hard stop de protection");
}
else if (sessionObservedActive) {
if (enteringCloudDebt) {
setCloudDebtStartedAt = true;
}
if (gridBatteryProtectionStop) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'stop_grid_battery_guard';
currentDecision = 'stop';
dynamicOk = false;
mustStop = true;
nextBlockReason = 'grid_battery_guard';
nextBlockedUntilStamp = formatParisStampFromMs(nowMs + 20 * 60000);
reason.push(`Arrêt HC réseau: batterie trop sollicitée (${round1(batteryCurrentA)}A)`);
}
else if (probeFailedNow) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'probe_failed_lock';
currentDecision = 'stop';
dynamicOk = false;
mustStop = true;
nextBlockReason = 'probe_failed';
nextBlockedUntilStamp = formatParisStampFromMs(nowMs + probeRetryMinutes * 60000);
reason.push("Mode sonde échoué");
}
else if (physicalCapA < currentMinA) {
if (minRunElapsed) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'stop_power_cap';
currentDecision = 'stop_power_cap';
dynamicOk = false;
mustStop = true;
nextBlockReason = 'battery_protection_stop';
nextBlockedUntilStamp = formatParisStampFromMs(nowMs + 10 * 60000);
reason.push("Capacité système insuffisante");
} else {
desiredCharging = true;
desiredPermission = true;
desiredLatch = true;
desiredCurrentA = currentMinA;
state = 'hold_min_run_power_cap';
currentDecision = 'hold';
dynamicOk = false;
mustStop = false;
reason.push("Maintien jusqu'au temps mini ON");
}
}
else if (probeWindowActive && !probeSuccessNow) {
if (!probeHoldGuardOk) {
desiredCharging = false;
desiredPermission = false;
desiredLatch = false;
desiredCurrentA = currentMinA;
state = 'probe_stop_guard';
currentDecision = 'stop';
dynamicOk = false;
mustStop = true;
nextBlockReason = 'probe_failed';
nextBlockedUntilStamp = formatParisStampFromMs(nowMs + probeRetryMinutes * 60000);
reason.push("Sonde interrompue: garde-fous non respectés");
} else {