feat(ups): ramura "pe baterie" se poate testa fara scoaterea din priza

Lantul de protectie are doua jumatati. Prima - "UPS-ul chiar raporteaza trecerea
pe baterie?" - e deja dovedita de perechile de evenimente Kernel-Power 105 de la
panele reale din 2026. A doua - decizie, alerta, oprirea curata a Oracle - nu
avea cum sa fie exercitata decat asteptand o pana.

Acum monitorul accepta un SIMULARE.json care suprascrie citirea de alimentare.
Doua garzi, ca un fisier uitat sa nu opreasca serverul pe date inventate:
"expira" e obligatoriu si e sters automat la depasire, iar oprirea e in gol daca
nu se cere explicit "dryRun": false. Cat e activa, fiecare ciclu scrie WARN in
log si -Mode Status o afiseaza.

test-battery.ps1 conduce simularea si urmareste logul; sterge fisierul si daca e
intrerupt. Cu -ConfirmReal 'DA-OPRESTE-SERVERUL' devine repetitie reala pentru o
fereastra de mentenanta - singurul mod de a masura cat dureaza shutdown immediate
pe baza de productie, informatie de care depinde alegerea pragurilor.

Validat in gol pe 27.08.2026: detectie la 15:36:09 + email de pana, apoi exact
trei cicluri de confirmare, declansare pe pragul de 35% la 15:36:40 si secventa
parcursa integral. Oracle a ramas Running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RRyaDj39hPQ89SZS6URRpS
This commit is contained in:
Marius
2026-08-27 15:37:48 +03:00
parent 9ba99eb9d1
commit 4b618dcb85
3 changed files with 245 additions and 8 deletions

View File

@@ -0,0 +1,121 @@
<#
.SYNOPSIS
Testeaza ramura "pe baterie" fara sa scoti UPS-ul din priza: scrie SIMULARE.json,
urmareste logul, apoi curata dupa el.
.DESCRIPTION
Se ruleaza PE SERVER, ridicat (Administrator), cu monitorul deja pornit.
Implicit, oprirea e parcursa IN GOL: se trimit emailurile, se scriu in log toti
pasii, dar nu se opreste nici Oracle, nici Windows. Asta valideaza tot lantul
decizie -> alerta -> secventa, adica exact partea pe care o pana reala ar
exercita-o.
Ce NU acopera: faptul ca Windows chiar raporteaza trecerea pe baterie. Acela e
deja dovedit de perechile de evenimente Kernel-Power 105 de la panele reale
(10 iun, 13 mai, 5 mai 2026) - vezi docs/ups-server36-monitorizare.md.
Simularea expira singura si e stearsa de monitor; scriptul o sterge oricum la
final, inclusiv daca e intrerupt.
.PARAMETER Percent
Incarcarea raportata. Sub pragul shutdownAtBatteryPercent (implicit 35),
declansarea vine dupa confirmPolls cicluri - adica in ~45 de secunde.
.PARAMETER Minute
Cat timp ramane activa simularea.
.PARAMETER ConfirmReal
Repetitia exacta 'DA-OPRESTE-SERVERUL' transforma testul intr-o repetitie
REALA: Oracle chiar se opreste si Windows chiar se inchide. De folosit doar
intr-o fereastra de mentenanta anuntata.
.EXAMPLE
# repetitie in gol - sigura oricand
powershell -ExecutionPolicy Bypass -File .\test-battery.ps1
.EXAMPLE
# doar trecerea pe baterie, fara sa se atinga pragul de oprire
powershell -ExecutionPolicy Bypass -File .\test-battery.ps1 -Percent 80 -Minute 3
.EXAMPLE
# repetitie REALA, in fereastra de mentenanta: opreste Oracle si serverul
powershell -ExecutionPolicy Bypass -File .\test-battery.ps1 -ConfirmReal 'DA-OPRESTE-SERVERUL'
#>
[CmdletBinding()]
param(
[string]$InstallDir = 'C:\ROMFAST\ups-monitor',
[ValidateRange(0, 100)][int]$Percent = 30,
[ValidateRange(1, 60)][int]$Minute = 4,
[string]$ConfirmReal
)
Set-StrictMode -Version 2.0
$ErrorActionPreference = 'Stop'
$simPath = Join-Path $InstallDir 'SIMULARE.json'
$real = ($ConfirmReal -eq 'DA-OPRESTE-SERVERUL')
if ($ConfirmReal -and -not $real) {
throw "-ConfirmReal trebuie sa fie exact 'DA-OPRESTE-SERVERUL'. Am primit: '$ConfirmReal'"
}
if (Test-Path -LiteralPath (Join-Path $InstallDir 'DEZACTIVAT')) {
Write-Warning 'Exista fisierul DEZACTIVAT: monitorul nu evalueaza nimic, deci testul nu va arata nimic.'
}
$cfg = Get-Content -LiteralPath (Join-Path $InstallDir 'config.json') -Raw -Encoding UTF8 | ConvertFrom-Json
$expira = (Get-Date).AddMinutes($Minute)
if ($real) {
Write-Host ''
Write-Host '*** REPETITIE REALA: Oracle SE VA OPRI si serverul SE VA INCHIDE. ***' -ForegroundColor Red
Write-Host ''
} else {
Write-Host 'Repetitie in gol: emailuri si log da, oprire efectiva nu.'
}
Write-Host ("Simulez: pe baterie, incarcare $Percent%. Prag de oprire: {0}% sau {1} min." -f `
$cfg.shutdownAtBatteryPercent, $cfg.shutdownAfterMinutesOnBattery)
Write-Host ("Simularea expira la {0:HH:mm:ss}." -f $expira)
Write-Host ''
$sim = [ordered]@{
onBattery = $true
upsPresent = $true
percent = $Percent
runtimeSec = 600
dryRun = (-not $real)
expira = $expira.ToString('o')
}
try {
$sim | ConvertTo-Json | Set-Content -LiteralPath $simPath -Encoding UTF8
Write-Host "Scris $simPath"
$log = Join-Path $InstallDir ('log\ups-monitor-{0}.log' -f (Get-Date -Format 'yyyy-MM'))
$de = 0
if (Test-Path -LiteralPath $log) { $de = @(Get-Content -LiteralPath $log).Count }
$pana = (Get-Date).AddMinutes($Minute)
while ((Get-Date) -lt $pana) {
Start-Sleep -Seconds 15
if (Test-Path -LiteralPath $log) {
$linii = @(Get-Content -LiteralPath $log)
if ($linii.Count -gt $de) {
$linii[$de..($linii.Count - 1)] | ForEach-Object { " $_" }
$de = $linii.Count
}
}
if (-not (Test-Path -LiteralPath $simPath)) {
Write-Host ' (simularea a expirat si a fost stearsa de monitor)'
break
}
}
} finally {
Remove-Item -LiteralPath $simPath -Force -ErrorAction SilentlyContinue
Write-Host ''
Write-Host 'Simulare oprita, citirea reala e din nou activa.'
Start-Sleep -Seconds 20
Write-Host '--- stare dupa test ---'
& powershell -NoProfile -ExecutionPolicy Bypass -File (Join-Path $InstallDir 'ups-monitor.ps1') -Mode Status
}

View File

@@ -113,6 +113,61 @@ function Get-PowerReading {
}
}
# ---------------------------------------------------------------------------
# Simulare - testarea ramurii "pe baterie" fara sa scoti UPS-ul din priza
# ---------------------------------------------------------------------------
#
# Cat timp exista SIMULARE.json langa config.json, citirea reala e suprascrisa
# cu valorile din el. Doua garzi, ca un fisier uitat sa nu opreasca serverul pe
# date inventate:
# - "expira" e OBLIGATORIU; fara el fisierul e ignorat, iar la depasire e sters;
# - oprirea e in gol (dry-run) daca nu se cere explicit "dryRun": false.
function Get-Simulation {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path)) { return $null }
try {
$s = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
} catch {
Write-Log "SIMULARE.json nu se poate citi, il ignor: $($_.Exception.Message)" -Level WARN
return $null
}
if (-not ($s.PSObject.Properties.Name -contains 'expira') -or -not $s.expira) {
Write-Log 'SIMULARE.json nu are camp "expira" - ignorat, ca sa nu ramana activ la nesfarsit' -Level WARN
return $null
}
if ((Get-Date) -gt [datetime]$s.expira) {
Write-Log 'SIMULARE.json a expirat - il sterg si revin la citirea reala' -Level WARN
Remove-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue
return $null
}
return $s
}
function Merge-Simulation {
param([object]$Reading, [object]$Sim)
$o = $Reading.PSObject.Copy()
$map = @{
onBattery = 'OnBattery'
percent = 'Percent'
runtimeSec = 'RuntimeSec'
upsPresent = 'UpsPresent'
}
foreach ($k in $map.Keys) {
if ($Sim.PSObject.Properties.Name -contains $k) {
$o.($map[$k]) = $Sim.$k
}
}
$o.AcKnown = $true
return $o
}
function Get-UpsDeviceInfo {
# Confirmare a doua, din WMI - folosita doar in alerte si in modul Status,
# ca sa nu interogam WMI la fiecare ciclu.
@@ -425,6 +480,17 @@ function Invoke-Cycle {
$now = Get-Date
$r = Get-PowerReading
# --- simulare (daca e activa, suprascrie citirea reala) -----------------
$sim = Get-Simulation -Path $script:SimPath
if ($sim) {
$r = Merge-Simulation -Reading $r -Sim $sim
$simDryRun = $true
if ($sim.PSObject.Properties.Name -contains 'dryRun') { $simDryRun = [bool]$sim.dryRun }
if ($simDryRun) { $DryRun = $true }
Write-Log ("SIMULARE ACTIVA pana la {0} (dryRun={1}): onBattery={2} percent={3} upsPresent={4}" -f `
$sim.expira, $simDryRun, $r.OnBattery, $r.Percent, $r.UpsPresent) -Level WARN
}
# --- comunicatia cu UPS-ul ---------------------------------------------
if (-not $r.UpsPresent) {
$script:CommsMissCount++
@@ -529,6 +595,7 @@ Urmeaza un email si atunci.
$cfg = Get-Config -Path $ConfigPath
$script:LogDir = $cfg.logPath
$script:SimPath = Join-Path (Split-Path -Parent $ConfigPath) 'SIMULARE.json'
$script:OnBatteryCount = 0
$script:CommsMissCount = 0
@@ -540,6 +607,13 @@ switch ($Mode) {
Write-Host ''
$st = Get-State -Path $cfg.stateFile
Write-Host "Stare salvata : power=$($st.powerState) comms=$($st.commsState) onBatterySince=$($st.onBatterySince)"
if (Test-Path -LiteralPath $script:SimPath) {
Write-Host "SIMULARE : ACTIVA -> $script:SimPath"
Get-Content -LiteralPath $script:SimPath -Raw | ForEach-Object { " $_" }
}
if (Test-Path -LiteralPath (Join-Path (Split-Path -Parent $ConfigPath) 'DEZACTIVAT')) {
Write-Host 'AUTOMATISM : DEZARMAT (exista fisierul DEZACTIVAT)'
}
try {
$task = Get-ScheduledTask -TaskName 'ROMFAST UPS Monitor' -ErrorAction Stop
$info = Get-ScheduledTaskInfo -TaskName 'ROMFAST UPS Monitor'