Kontaktné údaje
- info@ipesoft.com
- +421 907 703 854
- Obchodná 9076/3D
010 08 Žilina
Slovensko
© Copyright IPESOFT 2023
Pred približne rokom som sa zapojil do diskusie na Reddit-e. Istý Reddit užívateľ požadoval zápis niekoľkých meraných bodov do SQL databázy na zmenu hodnoty, s časovou značkou. Hodnota digitálnych signálov sa menila tak, že v stave ON bola cca 100 ms. Písal, že nemôže použiť SCADA systém, ktorý zvyčajne nemá dosť rýchly cyklus čítania.
Iný užívateľ v niekoľkých príspevkoch písal, že celkový čas akvizície dát (včítane zápisu do SQL databázy) nedokázal byť pod 200 ms. Môj návrh, aby použili Raspberry Pi, sa dočkal kritickej odozvy – vraj sa jedná o pomalé a prakticky nepoužiteľné zariadenie. A že podľa jeho skúseností, transakčný čas (včítane všetkej réžie) nebude stabilne pod 100 ms. Citujem:
Of course, I can be wrong, but as I'm based on my experience the transaction time less than 100 ms might be not stable on standalone computers with Windows OS that are mostly used for such tasks.
Nuž, 100 ms sa mi zdalo ako veľmi veľa. V minulosti pri skúšaní výkonu PostgreSQL archívu na Raspberry Pi sme dosahovali stovky zápisov za sekundu. To dáva menej ako 10 ms na zápis. Takže – ako je na tom naozaj PostgreSQL?
Preto som vyskúšal nakonfigurovať jednoduchú aplikáciu v D2000, ktorá by obsahovala Modbus TCP server so 100 meranými bodmi zverejňujúcimi hodnoty a Modbus klient, ktorý by ich raz za sekundu čítal. Následne chcem týchto 100 hodnôt každú sekundu zapísať do archívnej databázy PostgreSQL. Celý aplikačný server s komunikačným procesom, archívom aj databázou bude bežať na jednom počítači. Zvládne toho Raspberry?
Použitý hardvér
Poďme na to. Použil som Raspberry Pi 3 model B Plus (takže nie najnovšiu štvorku):
Tento model je osadený 1GB RAM a má 4-jadrový procesor na 1.3 GHz:
Nainštalovaná PostgreSQL verzia bola 9.6:
A D2000-ka bola vo verzii 12.0 Release 61.
Konfigurácia
Najskôr som si vytvoril 2 linky typu “TCP-IP/TCP”. Linka L.ModbusSrv počúvala na porte 502, na adrese ALL, čo je symbolický zápis pre počúvanie na všetkých sieťových rozhraniach. Linka L.ModbusCli mala nakonfigurované pripájanie sa na tento port 502 na lokálnom rozhraní.
Na linke L.ModbusSrv som vytvoril stanicu B.ModbusSrv, nakonfiguroval protokol Modbus Server a v záložke Address zadal adresu 1. Na linke L.ModbusCli som vytvoril stanicu B. ModbusCli, nakonfiguroval protokol Modbus Client a v záložke Address zadal opäť adresu 1. V časových parametroch som nastavil čítanie dát každú sekundu. Tu je pohľad na obidve stanice.
Na stanici B.ModbusSrv som vytvoril 2 výstupné merané body s adresami 3.0 a 3.1. To znamená, že Modbus Server bude poskytovať hodnoty týchto výstupných bodov pri žiadosti 3 (Read Holding Registers) z adries 0 a 1 (viď dokumentácia protokolu Modbus Server). Pomocou CSV exportu som exportoval konfiguráciu týchto meraných bodov do CSV súboru a v obľúbenom textovom editore PSPad, ktorý podporuje blokové kopírovanie, “namnožil” tieto body a vytvoril z nich 100 bodov s adresami 3.0 až 3.99, ako ukazuje nasledujúci obrázok:.
Ako riadiaci objekt som najskôr použil systémovy objekt sekunda (Sec), ale neskôr ho zmenil na užívateľskú premennú U.Sec, ktorú som menil v skripte približne dvakrát za sekundu (aby som v súlade so Shannon-Koteľnikovým teorémom zabezpečil dostatočne rýchle zmeny, aby každé čítanie zo strany klienta prečítalo odlišnú hodnotu). Hodnotu som menil od 0 po 59. Tu je príslušný skript E.IncrementTest vykonávaný procesom SELF.EVH, ktorý je tiež spustený na Raspberry Pi:
A tu je konfigurácia riadiaceho objektu pre jeden z výstupných meraných bodov:
Aby každý z výstupných meraných bodov dával inú hodnotu, nakonfiguroval som aj parametre lineárneho prevodu. Pre N-tý meraný bod (N=0..99) som chcel hodnotu 1000 * U.Sec + N.
Tj. napríklad bod M.ModbusSrv_14 by zverejňoval hodnotu 14, 1014, 2014 .. atď, až 59014.
Keďže sa ale jedná o výstupný meraný bod a parametre prevodu sú zadávané v smere vstupu z komunikácie, bolo nutné urobiť inverzný prepočet.
Takže ak Y = 1000 * X + N, potom X = Y/1000 - N/1000.
Takže prvý koficient (A) je 1/1000=0.001, druhý koeficient (B) je -N/1000 (pre každý meraný bod iný). Pre spomínaný bod M.ModbusSrv_14 bude prevod takýto:
Podobne ako výstupné body som na stanici B.ModbusCli vytvoril 2 vstupné merané body s adresami U3.0 a U3.1 a CSV exportom a importom ich namnožil na 100 meraných bodov. Tentokrát už žiadna konverzia nebola nutná. Písmeno U v adrese hovorí o interpretovaní prijatej hodnoty (2-bajtového Modbus registra) ako kladného čísla. Obrázok ukazuje body spolu s okamžitými hodnotami a adresami:
V každom cykle čítania sa vygeneruje jediná Modbus požiadavka na čítanie registrov 0 až 99, ktorú obslúži opäť jediná odpoveď.
Posledným krokom bolo vytvorenie dvoch archívov archivujúcich vstupné merané body a opätovné namnoženie pomocou CSV exportu a importu.
Testovanie
Výsledok celej námahy vidíme aj v systémovej štruktúre SV._System_ArchivPerformance. Ak do prvého stĺpca Name zadáme názov archívneho procesu SELF.ARC, začne D2000 každých 10 sekúnd zverejňovať štatistiky. Vidíme, že PerformedDatabaseRequest je 100. Takže každú sekundu archivujem 100 hodnôt (číslo niekedy vyskočí aj vyššie kvôli iným archívnym objektom, ktoré v tejto aplikácii existujú). Zároveň hodnota PendingDbRequest je 0 alebo blízko 0, takže SQL databáza stíha a vo fronte archívu sa nehromadia žiadne požiadavky na zápis.
Ako často sa vykonáva databázový COMMIT? Štandardná hodnota je po 1000 zapísaných hodnotách resp. aspoň raz za 60 sekúnd – tieto hodnoty sa dajú meniť, ale nerobil som to. Takže commitujeme každých 10 sekúnd.
Po cca. jednom dni prevádzky narástla hodnota DatabaseSize, udávajúca veľkosť archívnej databázy v MB, z 15 na 610. Tieto pokusy som robil na RPI s 16 GB pamätovou kartou, na ktorej bolo cca 10 GB voľného miesta. Takže ešte mi na pár dní vydrži.
Na ďalší deň ráno prekročila databáza 1GB.
Aká je záťaž RPI v takejto prevádzke? Toto je snímka krátko po nakonfigurovaní :
Takto vyzerá situácia po jednom dni:
Čísla sa menia, ale nad 1% CPU spotrebúvajú podľa očakávania procesy kernel, kom , archív a postgres.
Nechcený záťažový test
Keď som vytváral skript E.IncrementTest na inkrementáciu užívateľskej premennej U.Sec, samozrejme som sa pomýlil a zabudol som dať do skriptu oneskorenie (riadok „DELAY 500 [ms]“). Zároveň som pri konfigurácii U.Sec nevypol parameter „Ukladanie štartovacej hodnoty“. Dôsledok týchto dvoch chýb bol ten, že skript generoval hodnoty U.Sec tak rýchlo, ako stíhal, a D2000 Server ich ukladal ako štartovacie hodnoty do konfiguračnej databázy.
Čo je z našeho pohľadu zaujímavé – ako rýchlo boli tieto hodnoty ukladané? Opäť nám pomôže autodiagnostika D2000 systému. Systémová premená Pending_Cfg_Rq ukazuje počet konfiguračných zmien vo fronte na zápis (v našom prípade sa jedná o štartovacie hodnoty). Systémová premená Perf_Cfg_Rq ukazuje, koľko zápisov bolo priemerne vykonaných za posledných 10 sekúnd. Snímka CNF ukazuje, že za sekundu sa vykonalo vyše 600 zápisov, pričom vo fronte na zápis bolo vyše 6200 hodnôt:
Samozrejme, že po uvedomení si, čo som spáchal, som oneskorenie v skripte doplnil a zrušil aj ukladanie štartovacích hodnôt.
Test výkonnosti archívnej databázy
Po necelom dni prevádzky som chcel zistiť, že ako rýchlo vlastne PostgreSQL vie naše hodnoty archivovať. Na takéto záťažové testy má D2000 Archív podporu – existuje príkaz FREEZE, ktorý zastaví zápis na definovaný počet sekúnd (prvý parameter príkazu). Každú sekundu monitoruje frontu požiadaviek a vypisuje, koľko požiadaviek sa v nej nahromadilo. Po určenom čase odblokuje zapisovací task a pokračuje v monitorovaní (zadaný čas – druhý parameter príkazu). Situáciu po spustená FREEZE 60 15 ukazuje obrázok:
Vo fronte sa nahromadilo 5900 požiadaviek. Za prvú sekundu ich spracovania počet klesol na 4333, za druhú na 2538, za tretiu 742 a za štvrtú na 0. To znamená, že 5900 požiadaviek sa spracovalo za menej ako 4 sekundy. V skutočnosti bolo týchto požiadaviek o 400 viac, keďže komunikácia bežala ďalej a každú sekundu generovala ďalších 100 požiadaviek.
Časté zmeny malého počtu meraných bodov
Po jednom dni bezproblémového testovania som chcel otestovať inú konfiguráciu. Čo keby bolo tých bodov nie 100 ale iba 10, ale ich hodnoty by sa menili často? Ako často vieme meniť hodnoty niekoľkých bodov a ako často ich vieme čítať a archivovať?
Na linke L.ModbusCli som nakonfiguroval inú stanicu B.ModbusCliFast s adresou 2. Tentokrát som zadal v časových parametroch nulové oneskorenie (t.j. mód “čítaj ako rýchlo sa dá”). Upravil som aj parametre protokolu a zmenšil som všetky čakania na 1 ms. Na tejto stanici som vytvoril iba 10 meraných bodov.
Samozrejme som musel vytvoriť aj stanicu B.ModbusSrvFast s adresou 2 na linke L.ModbusSrv a príslušné výstupné merané body – tentokrát ale s riadiacim objektom U.SecFast. Tento som v skripte E.IncrementTestFast menil s oneskorením 10 ms (samozrejme, skutočné oneskorenie môže byť väčšie a závisí od implementácie).
Do systémovej štruktúry SV._System_StationPerformance som zadal názvy staníc B.ModbusCliFast a B.ModbusCli. Táto systémová štruktúra umožňuje získavať štatistiky o počtoch zmien na staniciach – čo je presne to, čo potrebujem.
Poznámka - archívne objekty pre Fast merané body som zatiaľ nevytváral.
Čo vidíme? Štatistiky za posledných 10 sekúnd ukazujú, že na meraných bodoch pod stanicou B.ModbusCli je očakávaných 1000 zmien za 10 sekúnd (zodpovedá jednej zmene každého zo 100 bodov za sekundu). Na stanici B.ModbusCliFast je okolo 7500 zmien za 10 sekúnd, čo zodpovedá 75 zmenám každého z 10 meraných bodov každú sekundu. Takže stanica B.ModbusCliFast stihne 75-krát za sekundu dotazovať stanicu stanici B.ModbusSrvFast, spracovať odpovede a zverejniť hodnoty. To je slušné .. na RPI.
Aká je záťaž Raspberry Pi v takomto režime? Podľa očakávania narástla záťaž KOM procesu (na obrázku takmer 55% výkonu jedného jadra CPU) – pôvodne mal cca 7%. Narástla aj záťaž D2000 Kernela (keďže všetky zmeny objektov musí spracúvať).
Aké je zaťaženie diskov? Utilita iotop ukazuje väčšinou zápis okolo 150 kB/sec.
Hodnota systémovej premennej Perf_Kernel_Rq (počet požiadaviek spracovaným D2000 Serverom za sekundu – jedná sa opäť o priemer z posledných 10 sekúnd) vyskočila na vyše 2200. Predtým bola na úrovni okolo 400.
Čo sa stane, keď týchto 10 rýchlych meraných bodov dáme do archívu? Zdvihneme jeho záťaž z 100 na 850 zápisov za sekundu. Budem to robiť postupne. Po vytvorení prvého archívu išla záťaž zo 100 na 175 zápisov:
Dva archívy –sme na 250, archív stíha:
Pridáme ešte dva, sme na 400:
Ďalšie dva archívy, takže už máme šesť. Záťaž je okolo 550 zápisov za sekundu.
Ešte dva, záťaž ide na 700.
A posledné dva, sme na 850 zápisoch do archívu za sekundu. Čo je dôležité – PendingDbRequest sa drží na 0, takže databáza stíha zapisovať.
Ako je na tom RPI? Záťaž archívu stúpla z 5% na takmer 40%. Postgres narástol zo 4% na 34%.
V tomto stave som nechal systém cca 5 minút, následne som vypol týchto 10 náročných archívov (najmä kvôli tomu, aby som si v noci nespotreboval všetko voľné miesto).
Ešte pohľad na archivované dáta. Takto vyzerá graf za 4 minúty, vidieť aj informačné okno, že načítaných bolo 18000 hodnôt, čo zodpovedá 75 hodnotám za sekundu.
A tu je tabuľka s hodnotami . Časové značky (ktoré Modbus protokol nepodporuje, takže ich prideľuje D2000 KOM proces pri čítaní) sú cca 11-15 ms vzdialené. Takže zareagovať na zmenu signálu s trvaním 100 milisekúnd by nemal byť problém:
Ďalší deň ráno som vyskúšal znovu zapnúť archiváciu rýchlych archívnych objektov a následne spustiť príkaz FREEZE 30 30 (tj. zastaviť archiváciu na 30 sekúnd a následne ďalších 30 sekúnd sledovať, ako sa nahromadené požiadavky spracúvajú). Za sekundu sa nazhromaždí cca 850 požiadaviek.
Čo vidíme? Za sekundu spracovania fronta poklesne o cca 400-600 požiadaviek. Nesmieme ale zabudnúť, že každú sekundu pribudne ďalších 850 hodnôt z komunikácie, takže celkový tok do archívu bol na úrovni 1250-1450 požiadaviek za sekundu.
Treba si uvedomiť ešte jednu veci. Na Raspberry Pi archív používa štandardne 1 zapisovací task. Takže ešte je výkonová rezerva tu. Skúsme nakonfigurovať 4 zapisovacie tasky nastavením parametra
SELF.ARC.WriteThreadsCount = 4
v súbore application.properties. Následne je potrebný reštart archívu.
Ak zopakujeme príkaz FREEZE 30 30, výsledok vidíte na obrázku.
Nazhromaždených cca. 25 tisíc požiadaviek sa spracovalo v priebehu 15 sekúnd, čo predstavuje priemerne 1650 požiadaviek za sekundu (plus ďalších 850 z komunikácie). To predstavuje špičkový výkon až 2500 zápisov za sekundu. Toto číslo udávam iba ako orientačné, keďže závisí od ďalších parametrov aplikácie, archívu, databázového servera PostgreSQL, ako aj od konkrétneho použitého modelu Raspberry Pi, včítane vlastností disku (na produkčné nasadenie by som určite uprednostnil exerný SSD disk pred SD kartou).
Keď som sa neskôr zamyslel nad tým, prečo máme „iba“ 75 zmien Fast bodov, došlo mi, že na jednej Modbus linke sú dve stanice (pomalá B.ModbusCli a nová B.ModbusCliFast). Pritom ale na pôvodnej stanici som nemenil časové parametre, čo spôsobí, že po poslaní výzvy sa čaká 100 ms na odpoveď. Takže posledný test – vypneme stanicu B.ModbusCli. Následne sa počet zmien v archívnej databáze dostal takmer na 950 za sekundu, čo zodpovedá 95 zmenám každého z 10 rýchlych meraných bodov. Tu už môžeme mať problém s tým, že nemeníme dosť často hodnoty výstupných bodov. Takže skúsme zmenšiť oneskorenie v skripte E.IncrementTestFast z 10 ms na 5 ms, takže za sekundu sa hodnota výstupných bodov zmení 200-krát. Čo sa stalo?
Dostali sme sa na hodnoty okolo 1290 – 1390 za sekundu. To znamená, že RPI stihne vykonať 129-139 čítacích požiadaviek za sekundu. Samozrejme, cez lokálne rozhranie, bez sieťovej latencie a podobných vplyvov. Ďalšie zmenšenie oneskorenia v skipte už nespôsobilo zväčšenie počtu zmien vstupných meraných bodov.
Takto vyzerajú hodnoty v archíve: časové značky sú 4-9 ms od seba, aspoň každá druhá zmena hodnoty je zachytená.
V tomto režime je pochopiteľne vyššia záťaž všetkých procesov – kom išiel nad 80%, archív tesne pod 80%, kernel nad 50% a dva zapisovacie tasky PostgreSQL databázy spolu takmer 70%. Áno, znížil som počet zapisovacích taskov zo 4 na 2, ale bez výrazného účinku.
Záver
Môžem iba špekulovať, prečo mal spomínaný užívateľ taký negatívny pohľad na výkon Raspberry Pi. Je možné, že skúšal zber dát pomocou aplikácie s jedným taskom, ktorá zároveň komunikovala, spracovávala dáta a ukladala ich do databázy. Technológia D2000 je modulárna, využíva multithreading a multitasking, asynchrónnu komunikáciu pomocou prioritných front (queues) a tak efektívne využíva prostriedky počítača k maximalizácii výkonu a uprednostneniu časovo kritických činností.
V tomto blogu som chcel ukázať, aké možnosti a zároveň aké limity má SCADA aplikácia nasadená na Raspberry Pi. Myslím si, že požiadavky na zber a archiváciu niekoľkých digitálnych signálov, ktoré sa menia na cca 100 milisekúnd, nie je problém dosiahnuť. V prípade potreby by bolo možné napr. zvýšiť prioritu D2000 KOM procesu - na úkor archivácie a kernela, databázy a ďalších procesov, ktorým nevadí prerušenie na niekoľko (desiatok) milisekúnd.
Niektorí z našich zákazníkov už priemyselné klony Raspberry (ako sú napr. Unipi alebo NPE X500) používajú - práve ako komunikačné počítače v prevádzke. Spolu s vlastnosťou KOM Archív, ktorá umožňuje procesu D2000 KOM pracovať v režime offline (bez pripojenia k procesu D2000 Server – napríklad pri výpadkoch siete) a ukladať hodnoty získané z komunikácie do súboru (archívu) pre ich neskoršie odoslanie do systému D2000, sa jedná o efektívne a spoľahlivé riešenie komunikácie.
Keď hovorím o efektivite, tak myslím na viacero parametrov - výkon, cenu, spotrebu energie. Keď hovorím o spoľahlivosti, tak v prípade použitia ako Raspberry Pi ako aplikačného servera vieme ponúknuť aj redundantnú prevádzku s použitím dvoch alebo viacerých Raspberry Pi. Takáto konfigurácia významne znižuje riziko predstavované výpadkom aplikačného servera – či sa jedná o Raspberry Pi, priemyselný počítač alebo štandardný server. Zároveň je použitie Raspberry Pi efektívne v prostredí menších aplikácií (napr. výmenníčky tepla, vodárne a iné malé priemyselné aplikácie s rozsahom rádove stoviek tagov).
Ak Vás tento blog zaujal a chceli by ste si vyskúšať porovnať výkon D2000 na Vašom zariadení (či už RPI alebo inom počítači), môžete to jednoducho spraviť. Ak máte RPI, stiahnite si image pre RPI a nainštalujte ho podľa návodu. Následne si stiahnite XML export všetkých objektov, ktoré som vytvoril pre potreby tohto blogu.
11. február 2021, Ing. Peter Humaj, www.ipesoft.com