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:
110
archive/decoupesurplus/V2/batcloudP12.js
Normal file
110
archive/decoupesurplus/V2/batcloudP12.js
Normal file
@@ -0,0 +1,110 @@
|
||||
// =====================================================
|
||||
// CONTEXT HELPERS
|
||||
// =====================================================
|
||||
const houseSpikeThresholdW = 1500;
|
||||
const houseSpikeDetected =
|
||||
(parsed.houseAcPowerW - parsed.houseAcPowerPrevW) >= houseSpikeThresholdW;
|
||||
|
||||
const solarPotentialContext =
|
||||
context.sourceMode === 'solar_potential' ||
|
||||
(
|
||||
parsed.soc >= 80 &&
|
||||
derived.forecastBufferKwh > 0 &&
|
||||
parsed.pvPowerW < context.effectivePvStartMinW &&
|
||||
derived.houseNonEvcsW < 1200
|
||||
);
|
||||
|
||||
// =====================================================
|
||||
// REASON-BASED LOCKS
|
||||
// =====================================================
|
||||
const nextWindowCandidates = [
|
||||
nextDailyOccurrenceMs(params.morningStartMin, nowMs),
|
||||
nextDailyOccurrenceMs(params.hcMiddayStartMin, nowMs),
|
||||
nextDailyOccurrenceMs(params.eveningStartMin, nowMs),
|
||||
nextDailyOccurrenceMs(params.hcNightStartMin, nowMs),
|
||||
].filter(v => Number.isFinite(v));
|
||||
|
||||
const nextAllowedWindowStartMs =
|
||||
nextWindowCandidates.length > 0 ? Math.min(...nextWindowCandidates) : null;
|
||||
|
||||
const blockActive =
|
||||
history.blockedUntilMs !== null &&
|
||||
history.blockedUntilMs > nowMs + 1000;
|
||||
|
||||
const blockStillRelevant =
|
||||
history.lastBlockReason === 'outside_window'
|
||||
? context.profile === 'outside_window'
|
||||
: history.lastBlockReason === 'probe_failed'
|
||||
? context.sourceMode === 'hybrid_probe'
|
||||
: history.lastBlockReason === 'battery_protection_stop'
|
||||
? (
|
||||
parsed.batteryCurrentA < -params.batteryNeutralBandA ||
|
||||
derived.anyVebusLowBattery ||
|
||||
derived.vebusDcVoltageSoftLow
|
||||
)
|
||||
: history.lastBlockReason === 'grid_battery_guard'
|
||||
? isGridSource
|
||||
: history.lastBlockReason === 'vebus_overload_persistent'
|
||||
? derived.anyVebusOverload
|
||||
: false;
|
||||
|
||||
const startBlockedByReason = blockActive && blockStillRelevant;
|
||||
|
||||
// =====================================================
|
||||
// CLOUD DEBT
|
||||
// =====================================================
|
||||
const batteryNetPowerW = parsed.batteryCurrentA * derived.batteryVoltageRef;
|
||||
let cloudDebtNextKwh = params.cloudDebtPrevKwh;
|
||||
|
||||
if (sessionObservedActive) {
|
||||
if (batteryNetPowerW < 0 && !isGridSource) {
|
||||
cloudDebtNextKwh += (Math.abs(batteryNetPowerW) * cfg.sampleSeconds) / 3600000;
|
||||
} else if (batteryNetPowerW > 0) {
|
||||
cloudDebtNextKwh -= (batteryNetPowerW * cfg.sampleSeconds) / 3600000;
|
||||
}
|
||||
} else if (batteryNetPowerW > 0) {
|
||||
cloudDebtNextKwh -= (batteryNetPowerW * cfg.sampleSeconds) / 3600000;
|
||||
}
|
||||
|
||||
cloudDebtNextKwh = clamp(cloudDebtNextKwh, 0, params.cloudDebtMaxKwh * 4);
|
||||
|
||||
const cloudDebtMeaningful =
|
||||
cloudDebtNextKwh > cfg.cloudDebtMeaningfulThresholdKwh;
|
||||
|
||||
const cloudEventDischargingNow =
|
||||
sessionObservedActive &&
|
||||
!isGridSource &&
|
||||
parsed.batteryCurrentA < -Math.max(1, params.batteryNeutralBandA) &&
|
||||
cloudDebtMeaningful;
|
||||
|
||||
const continuingCloudEvent =
|
||||
cloudEventDischargingNow &&
|
||||
history.cloudDebtStartedMsPrev !== null &&
|
||||
params.cloudDebtPrevKwh > cfg.cloudDebtRecoveryThresholdKwh;
|
||||
|
||||
const enteringCloudDebt =
|
||||
cloudEventDischargingNow && !continuingCloudEvent;
|
||||
|
||||
const cloudDebtActive = cloudEventDischargingNow;
|
||||
|
||||
const cloudDebtStartedMs =
|
||||
cloudEventDischargingNow
|
||||
? (continuingCloudEvent ? history.cloudDebtStartedMsPrev : nowMs)
|
||||
: null;
|
||||
|
||||
const cloudDebtDurationMinutes =
|
||||
cloudDebtActive && cloudDebtStartedMs !== null
|
||||
? Math.max(0, (nowMs - cloudDebtStartedMs) / 60000)
|
||||
: 0;
|
||||
|
||||
const effectiveCloudToleranceMinutes =
|
||||
params.cloudToleranceMinutes +
|
||||
(solarPotentialContext ? batteryTune.global.solarPotentialGraceMinutes : 0) +
|
||||
(houseSpikeDetected ? batteryTune.global.houseSpikeGraceMinutes : 0);
|
||||
|
||||
const cloudToleranceOk =
|
||||
cloudDebtNextKwh <= params.cloudDebtMaxKwh &&
|
||||
(
|
||||
!cloudDebtActive ||
|
||||
cloudDebtDurationMinutes <= effectiveCloudToleranceMinutes
|
||||
);
|
||||
Reference in New Issue
Block a user