26.3.2024

Priebežná integrácia v archíve alebo ako na zubaté grafy

Pred pár dňami prišiel za mnou kolega. Že či by som mu neporadil: „Potrebujem sumovať, respektíve integrovať spotrebu energie – ale tak, že na začiatku každej pracovnej zmeny sa to číslo vynuluje. Takže graf toho bude pripomínať zuby píly – bude rásť, rásť a potom na hrane pracovnej zmeny pôjde znovu na nulu. Viem to urobiť nejak elegantne – najlepšie priamo v archíve a bez nutnosti počítania niekde v ESL skripte?“.

 

Pokiaľ by zadanie znelo tak, že chce integrál za 8-hodinovú pracovnú zmenu, nebolo by nutné nič vymýšľať. Jednoducho by sa nakonfiguroval štatistický archív , ktorý by počítal integrál z primárneho archívu:

Obr 1: pri konfigurovaní štatistického archívu je nutné najskôr zadať účel archívu (štatistický)  a vybrať archívny objekt, ktorého štatistika sa bude počítať 
Obr  2: na záložke s časovými parametrami nastavíme periódu a ofset archivácie
(v príklade je použitá 1 minúta, v skutočnosti by to bolo 8 hodín)

Obr 3: na poslednej záložke vyberieme typ štatistickej funkcie – integrál a zvolíme časové jednotky (ak je meraný bod v kilowattoch, máme na výber integrál v kilowatthodinách, kilowattminútach alebo kilowattsekundách).
Obr 4: zobrazenie hodnôt štatistického archívu v tabuľke –hodnoty sú s minútovou periódou.

 Ako vidieť na obrázku vyššie, takýto archív by sa automaticky prepočítal každú minútu. Ale požiadavky kolegu boli iné – integrácia má prebiehať priebežne a užívateľovi sa majú zobrazovať aj medzivýsledky a nielen jedno číslo na konci periódy (8-hodinovej pracovnej zmeny).

 

Takže štatistický archív nebude to pravé orechové. Cestou k riešeniu bude zrejme použiť vypočítaný archív a dať mu požadované vlastnosti.

 

Aby som mohol príklad ladiť, vytvoril som si linku, stanicu so zapnutou simuláciou a periódou pollingu 2 sekundy a meraný bod M.MySimulPower. Tento meraný bod bude meniť hodnotu podľa časových parametrov stanice, t.j. cca každé 2 sekundy (pričom hodnoty budú mať sínusový priebeh s amplitúdou rešpektujúcou nastavenie limitov LL a HL na meranom bode). Následne som vytvoril primárny archív H.MySimulPower, ktorý archivuje tento meraný bod:

Obr 5: primárny archív archivujúci hodnotu meraného bodu M.MySimulPower

Potom som sa pustil do tvorby vypočítaného archívu. Princíp integrácie je jednoduchý. Integrál (resp. suma) sa skladá zo súčtov Hodnota * DeltaT, kde DeltaT je veľkosť časového intervalu, počas ktorého bola hodnota platná. Prvá verzia vypočítaného archívu vyzerala takto:

Obr 6: vypočítaný archív implementujúci jednoduchý integrál

Riadok
_prevVal := %ARC_GetValue(H.MySimulPowerIntegral, @EvalTime - 0.001, @FALSE)
zistí predchádzajúcu hodnotu integrálu.

 Čas je špecifikovaný ako čas výpočtu (reprezentovaný konštantou @EvalTime) zmenšený o 1 ms, čo je základná časová jednotka podporovaná systémom D2000). Parameter @FALSE hovorí, že ak pre požadovaný čas nie je k dispozícii hodnota, použije sa najbližšia staršia.

 

Riadok
_prevTime:= _prevVal\TIM
priradí do premennej _prevTime časovú značku predchádzajúcej hodnoty integrálu.

 

No a nakoniec riadok
_prevVal + (@EvalTime - _prevTime) * H.MySimulPower
v sekcii FINALLY vykoná integráciu – k predchádzajúcej hodnote integrálu pričíta aktuálnu hodnotu integrovanej veličiny H.MySimulPower vynásobenú veľkosťou časového intervalu (aktuálny čas mínus čas poslednej naintegrovanej hodnoty).

 

Takýto integrál ale nespĺňa požiadavky zadania, pretože stále rastie. Takže ako implementovať požadované vynulovanie  na začiatku ďalšej zmeny?

 

 Pre zrýchlenie ladenia nebudeme nulovať po 8 hodinách ale po naše pracovné zmeny budú 15 sekundové. Niekomu by takáto pracovná zmena vyhovovala aj v skutočnom živote :-)

 

Ako na to? Najskôr si vytvoríme pomocný archívny objekt H.MyPeriodicTrigger, ktorý bude mať požadovanú periódu (15 sekúnd). Tento použijeme na vybudenie výpočtu na hrane pracovnej zmeny. Tento 15-sekundový archív môže archivovať hocičo – konkrétne sme použili systémový objekt Day, ktorý sa mení raz denne a udáva aktuálny deň v mesiaci.

 

Niektorí čitatelia môžu namietnuť, že takto si zbytočne zaplníme archívnu databázu – každých 15 sekúnd archivovať tú istú hodnotu? Táto námietka môže byť platná v iných systémoch ale nie v D2000. Vďaka zmenovému spôsobu ukladania periodických dát v  archívnej databáze D2000 sa každý deň uloží iba jediná hodnota.

Obr 7: konfigurácia primárneho periodického archívu, ktorý archivuje Day

Dôležité je aj nastavenie časových parametrov – dĺžka pracovnej zmeny (15 sekúnd) a ukladanie času konca intervalu.

Obr 8: Časové parametre archívneho objektu H.MyPeriodicTrigger

 Ako sa zmení vypočítaný archív? Odpoveď je na nasledujúcom obrázku:

Obr 9: vypočítaný archív implementujúci integrál vynulovaný na začiatku intervalu. 

 Jednak do výpočtu potrebujeme zahrnúť trigger H.MyPeriodicTrigger (hoci hodnotu tohto objektu vôbec nepoužijeme, ale vybudí nám výpočet). To robí riadok
_prevVal := H.MyPeriodicTrigger

 

Ďalší riadok (ktorý ostal bez zmeny) túto hodnotu prepíše predchádzajúcou hodnotou integrálu.
_prevVal := %ARC_GetValue(H.MySimulPowerIntegral, @EvalTime - 0.001, @FALSE)

 

Ďalej pribudli riadky implementujúce resetovanie. Ak je čas predchádzajúcej hodnoty integrálu násobok periódy, tak sa výsledok vynuluje (lebo počítame prvú hodnotu v novej pracovnej zmene). Inak sa nič nestane, teda použije sa predchádzajúca hodnota (pokračujeme v integrovaní).
IF %ModTime(_prevTime, 15) = 0 THEN
_prevVal := 0
ENDIF

 

Posledný riadok implementujúci integráciu zostal bez zmeny.

Obr 10: Graf zobrazujúci hodnoty upraveného archívu ukazuje, že maximá sú v násobkoch 15 sekúnd a následne ďalší výpočet integruje opäť od nuly. 

Je tento predpis už finálny, alebo mu do dokonalosti ešte niečo chýba? Napadajú ma dve možné vylepšenia:

  • Zaškrtnutie voľby “Replace Invalid values with 0”, čo spôsobí, že príchod neplatnej hodnoty z komunikácie nezneplatní výpočet integrálu, ale bude sa integrovať nula.
  • V prípade, že by sa hodnota z komunikácie dlhšie nemenila, tak užívatelia by sa pozerali na konštantnú hodnotu integrálu. Pre tento prípad je možné “oživiť” výpočet tak, že sa podstatne zmenší perióda triggra MyPeriodicTrigger (u nás z 15 sekúnd napr. na 5 sekúnd, v produkcii z 8 hodín tiež na 5 sekúnd). Táto zmena zabezpečí prepočet integrálu raz za zvolenú periódu, aj keď sa hodnota samotného meraného bodu nezmení. Aby sa nepokazilo nulovanie na začiatku pracovnej zmeny, tak pracovná zmena (8 hod) musí byť násobkom periódy triggra. Takže 1,2,3,4,5, 6 alebo 10 sekúnd je v poriadku, 7 sekúnd by nefungovalo.
Obr 11: po zmene parametrov simulácie na 1 hodnotu za 20 sekúnd sa prejavia pravidelné 5-sekundové prepočty vyvolané triggrom H.MyPeriodicTrigger. V čase 14:37 vidieť vynulovanie integrálu a zápis “skoro nuly” spôsobený zmenou hodnoty meraného bodu tesne po 14:37.

Záver

Popisovaný vypočítaný archív demonštruje silu archívneho subsystému aplikačného servera D2000 jednoducho realizovať užívateľské výpočty – iba prostredníctvom archívu, bez použitia akýchkoľvek externých prostriedkov.

 

Keď som sa na veľtrhu Ampér 2019 rozprával s technikmi renomovaného rakúskeho dodávateľa PLC a riadiacich systémov a pýtal som sa ich, ako by realizovali funkčnosť “vypočítaných archívov”, tak rozprávali o externom skripte napísanom v Pythone, ktorý by periodicky načítaval dáta primárnych archívnych objektov, počítal a výsledky posielal do archívu.

 

Iný, tentokrát americký výrobca SCADA systémov, používa periodicky spúšťané externé programy v jazyku C, ktoré robia niečo podobné ako Python skripty.

 

Niektoré nevýhody týchto prístupov sú zjavné na prvý pohľad – namiesto jednoduchej konfigurácie v natívnom prostredí SCADA systému (používajúcej rovnakú syntax ako vypočítané body či ESL skripty) musí aplikačný programátor používať iné jazyky (Python, C) a venovať úsilie nielen napísaniu samotného výpočtu, ale aj získaniu dát a zapísaniu výsledkov.

Ďalšie nevýhody sú možno viac skryté. Externé výpočty treba pri upgradoch, zmenách verzie a prípadne pri migráciách na inú platformu udržovať a prípadne rekompilovať (kvôli zmenám v API alebo kvôli linkovaniu aktuálnej API knižnice). Zdrojové kódy je potrebné niekde udržovať a spravovať. V redundantných systémoch je potrebné distribuovať aktuálne verzie externých skriptov alebo binárok na dva či viaceré servery a riešiť, kde sú najnovšie zdrojové kódy.

 

Vyčlenenie výpočtov mimo SCADA systém navyše spôsobí stratu väzieb medzi zdrojovými a vypočítanými archívnymi objektami. Jedna z kľúčových vlastností systému D2000 nazývaná referenčná integrita je založená na udržovaní si informácií o väzbách medzi všetkými objektami v systéme. O každom objekte je neustále dostupná informácia, ktoré iné objekty používa a ktoré objekty používajú jeho. Nemôže tak dôjsť k zrušeniu ešte používaného objektu. Zároveň je referenčná integrita neoceniteľná pomôcka pri ladení a hľadaní chýb (dáva odpovede na otázky typu “ako a odkiaľ sa sem tá hodnota dostala”) a pri budovaní a najmä udržovaní riešení s rozsahom desaťtisíce až stotisíce objektov.

 

Dnešným príkladom z praxe som chcel demonštrovať, ako sa v D2000 dajú relatívne jednoducho riešiť netriviálne problémy – s použitím zabudovanej funkcionality vypočítaných archívov a bez nutnosti uchyľovať sa k barličkám typu externých programov.

2.5.2019, Ing. Peter Humaj, www.ipesoft.com

Iné blogy