R2: git = source de vérité — src/nodes/ + build/extract intégrés au push
- src/nodes/*.js (6 nodes + template snapshot) = source de vérité unique. - n8n-push.mjs reconstruit le JSON depuis src/ avant syntaxe+golden+PUT (src gagne toujours; n8n-extract.mjs pour resynchroniser si édition UI). - n8n-build.mjs --check pour vérifier la synchro. - Copies historiques déplacées dans archive/ (mortes, dérivées de la prod). - .gitignore: backups locaux de rollback. - AGENTS.md: règle R2 pour toutes les futures sessions (dont Qwen local). - Validé: idempotence, mutation/restauration, pipeline push complet en réel.
This commit is contained in:
155
archive/decoupesurplus/DECISION_ENGINE_4.1.js
Normal file
155
archive/decoupesurplus/DECISION_ENGINE_4.1.js
Normal file
@@ -0,0 +1,155 @@
|
||||
// === 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 {
|
||||
Reference in New Issue
Block a user