Egy háztartási napelemes rendszer négy év adata, három forrásból, egy adatbázisba — és mi derült ki közben. A munkát egy AI-ügynök végezte, felügyelet mellett; a lap többek közt arról szól, hogy hol tévedett.
Fronius Primo 5.0-1 inverter, Datamanager 2.0 adatgyűjtővel, 4,48 kWp modul két füzérben: délkeletre 3200 Wp, délnyugatra 1920 Wp. Üzembe helyezés 2019 júliusában, egyfázisú betáplálás, HMKE-szerződéssel. A monitorozás a gyártó felhőszolgáltatásán keresztül ment — ott viszont az adat mögé nem lehet benézni, és összevetni sem lehet a mérőóra adatával.
A cél az volt, hogy ugyanabban az adatbázisban legyen ott, hogy mit termelt a tető és mit mért az elszámolási mérő. Ez három, egymással semmiben nem kompatibilis forrást jelentett.
A Datamanager a helyi hálózaton kiszolgál egy JSON API-t. Öt perces bontásban adja vissza a teljesítményt, a termelt energiát, az inverter hőmérsékletét, az AC feszültséget, és — ez a lényeg — füzérenként a DC áramot és feszültséget.
GET /solar_api/v1/GetArchiveData.cgi
?Scope=Device&DeviceClass=Inverter&DeviceId=1
&StartDate=09.06.2024&EndDate=23.06.2024&Channel=...
Két buktató. A Scope=System hívás minden 15 napos tartományon
Status.Code 255 hibával elszáll — a Scope=Device ugyanarra az adatra
hibátlanul válaszol. A válaszban pedig az idősor kulcsai nem időbélyegek, hanem
másodpercek a tartomány kezdetétől; ezt a hívó dolga visszafejteni.
Eredmény: 129 964 darab ötperces rekord és 259 928 füzérmérés, 2024 júniusától. Ennél korábbra a készülék nem őriz adatot.
Az elosztó ügyfélportálja egy SAP OData szolgáltatás mögött ül. A negyedórás mérési sorozat lekérdezhető, ha az ember eltalálja a paraméterezést:
MeasData(Pod='…',MeasVarList='+A,-A',Interval='1',
StartDate=datetime'…',EndDate=datetime'…')
?$expand=MeasDatas&$format=json
A portál felületén a mérési csatornák listája elsőre üresnek látszik. Nem hiba: a csatornák kulcsa a (csatorna, időfelbontás, szakág) hármas, tehát amíg nincs kiválasztva időfelbontás, nincs mit felkínálni. Erre a tulajdonos jött rá, nem az ügynök — az ügynök addigra már „portálhibát” állapított meg. Az API válasza utólag igazolta a tulajdonost.
Eredmény: 134 894 negyedóra, 2022 októberétől.
Kiderült, hogy évekre visszamenőleg érkeztek xlsx-mellékletek ugyanezekkel az adatokkal. 198 696 mért érték és 7 991 mérőállás jött össze belőlük. Ez első ránézésre redundáns másolatnak tűnt. Nem az volt — lásd a következő szakaszt.
A betöltött hálózati adat minden formai ellenőrzésen átment. Minden napra megvolt mind a 96 negyedóra, nem volt hiányzó sor, nem volt típushiba. Ettől függetlenül 2023 első felében 151 nap adata csupa nulla volt, és 2024 decemberében további 22 napé.
Az ok egyszerű, a következménye nem: a mérőoldal a „nincs adat” állapotot üres mezővel küldi, amiből a betöltő nullát csinál. A nulla pedig legitim érték — nem hiányzik, nem gyanús, nem dob hibát. Egy lakott házban viszont nincs olyan negyedóra, amelyben egyszerre nulla a vételezés és nulla a betáplálás: 1410 napból 1357-en egyetlen ilyen negyedóra sincs.
A javítás az e-mailes archívumból jött. Utána a korábbi évek fogyasztása így módosult:
Utólag ellenőrizve az elosztónál sincs meg ez az adat: a 2023 márciusi közvetlen lekérdezés 768 negyedórára 768 üres értéket adott vissza, míg a kontroll-hónap teljesen kitöltött volt. Az e-mailes archívum tehát nem másodpéldány, hanem az egyetlen fennmaradt forrás.
A tanulság nem az, hogy „ellenőrizni kell az adatot”. Az ellenőrzés megvolt, és átment. A tanulság az, hogy a formai ellenőrzés egy egész osztálynyi hibát elvben sem lát meg, és ezekre csak egy független forrás derít fényt.
Az exportok helyi időben érkeznek, ezért az őszi ismétlődő óra kétszer szerepel bennük, a tavaszi kimaradó óra pedig hiányzik. Az adatbázisban minden UTC-ben van, a helyi idő származtatott. Ebből következik, hogy egy nap nem mindig 96 negyedóra: tavasszal 92, ősszel 100 — ezt a teljességi ellenőrzés kiszámolja, nem beégetve tárolja.
Az e-mailes táblák időbélyege az intervallum végét jelöli, az adatbázisé a kezdetét. A −15 perces eltolás után 2025 júliusára 2818 negyedórából 2818 egyezett pontosan, 34 hónapon át pedig 153 358 érték — 32 összevethető hónapból 20 hibátlanul.
A negyedórára kerekítés kézenfekvő módja UNIX_TIMESTAMP és
FROM_UNIXTIME párosa. Ez viszont a kapcsolat időzónáján keresztül dolgozik, tehát az
őszi ismétlődő órában kétértelmű. A helyes forma tiszta dátumaritmetika:
ts_utc - INTERVAL (MINUTE(ts_utc) % 15) MINUTE
- INTERVAL SECOND(ts_utc) SECOND
Ez az összesítő első változatában még hibás volt, és egy RIGHT JOIN is benne
volt, ami a termelést hálózati adat nélkül tartalmazó negyedórákat kidobta volna. Mindkettő
felülvizsgálat közben derült ki, betöltés előtt.
18 tábla, 64,5 MB, MariaDB. A lényeg a rétegzés: nyers forrástáblák külön (
fronius_5min, fronius_string_5min, grid_15min,
meter_reading_15min), fölöttük egy közös negyedórás réteg
(energy_15min) forrásmaszkkal, majd napi és havi összesítők. A forrásmaszk minden
negyedórára megmondja, melyik forrásból van adat — ez az, ami később lehetővé teszi, hogy a
hiányt hiányként, ne nullaként közöljük.
Minden betöltő idempotens és STRICT_TRANS_TABLES mellett fut, hogy egy
tartománytúllépés hibát dobjon, ne csendben csonkoljon. Ez mérve is lett: a betöltők második
futása után minden sorszám és minden összeg bitre azonos maradt.
A nyilvános oldal egyáltalán nem nyúl az adatbázishoz. Éjjel egy cron
lefuttat egy generátort, ami statikus JSON-fájlokba írja ki a tegnapig tartó napi és havi
adatot; az oldal ezeket olvassa. Így nincs SQL-injekciós felület, nincs adatbázis-terhelés, és
a lap statikus fájlként szolgálható ki. Az írás átmeneti fájlba megy, majd
rename()-mel cserélődik, hogy az olvasó soha ne lásson fél fájlt.
A friss, 5–15 perces adat külön, tokennel védett végponton érhető el, csak a tulajdonosnak. A vágás a szerveren történik: a mai nap fizikailag nem kerül bele a nyilvános fájlokba. A nyilvános felbontás azért napi és havi, mert a negyedórás fogyasztási görbe megmutatná a háztartás napi rutinját — ébredés, távollét, autótöltés.
Az ügynök írta a lekérdezéseket, a betöltőket, a sémát és ezt az oldalt; ő találta meg a nullás időszakokat, és ő futtatta le az ellenőrzéseket. Amit viszont érdemes tisztán látni:
A minta mindegyikben ugyanaz: a hasznos munka nagy részét a gép végezte, a hibák pedig ott buktak ki, ahol volt független ellenőrzés — másik adatforrás, másik jel, másik szempár. Ahol nem volt, ott a hiba magabiztosan és jól formázva jelent meg.