\chapter{Architektúra vlastného systému}\label{chp:architecture}

Vlastný systém sa skladá z~dvoch samostatných, ale navzájom prepojených
subsystémov a~multiplexeru zariadení využívajúcich protokol Modbus.

\begin{figure}[!h]
    \centering
    \resizebox{!}{19.5cm}{\input{Diagrams/Architecture.latex}}
    \caption{Diagram architektúry systému}\label{fig:archdiagram}
\end{figure}

\section{Subsystém pre~zber a~archiváciu údajov}

Samotný subsystém pre~zber a~archiváciu údajov má za~úlohu zbieranie
časozberných dát z~jednotlivých technologických celkov a~ich ukladanie
v~databázovom systéme.
Okrem tejto hlavnej úlohy taktiež poskytuje rozhranie databázy pre~druhý
subsystém.
Subsystém nie je monolitického charakteru a~tiež sa skladá z~niekoľkých častí.

\subsection{Databázový systém}

Hlavnou časťou tohto subsystému je databázový systém, v~ktorom sú uložené
zozbierané dáta spolu s~ich časovou známkou.
Zároveň poskytuje užívateľské rozhranie pre~jednoduché zobrazenie uložených dát
a~programové rozhranie umožňujúce prístup k~historickým dátam inými časťami
systému.

\subsection{Systém zberu dát z~technologických celkov}

Systém zberu dát je samostatná služba, ktorá pomocou pluginov zbiera dáta
z~technologických celkov a~ukladá ich do databázového systému.
Vykonáva to dvomi spôsobmi v~závislosti na~možnostiach daného protokolu.
Prvý spôsob je zber dát v~pravidelných intervaloch pomocou dotazov adresovaných
predmetným zariadeniam v~technologických celkoch.
To je prípad protokolov SNMP, HTTP a Modbus.
Druhý spôsob je čakanie na~správy o~zmene hodnoty alebo vykonanej akcii
od~samotných zariadení v~technologických celkoch.
Túto možnosť podporujú protokoly BACnet, HTTP a~signály protokolu SNMP.

\section{Subsystém pre~vizualizáciu a~ovládanie}

Úlohou druhého subsystému je vizualizácia schém technologických celkov a~ich
manuálne ovládanie.
Tento subsystém má dva hlavné komponenty.

\subsection{Backend}

Backend beží ako služba na~serveri a~frontendu predáva aktuálne hodnoty
zariadení z~technologických celkov a~taktiež od~neho prijíma požiadavky
na~manuálnu zmenu týchto hodnôt alebo vykonanie inej akcie technologického
celku.
S~týmito zariadeniami komunikuje pomocou modulov pre~jednotlivé protokoly.

\subsection{Frontend}

Frontend poskytuje používateľské rozhranie a~zobrazuje používateľovi schému
vybraného technologického celku s~aktuálnymi hodnotami zo~zariadení.
Taktiež prijíma požiadavky používateľa na~vykonanie akcie v~technologickom
celku, napríklad manuálne nastavenie hodnoty zariadenia, a~preposiela ich
pomocou programového rozhrania backendu.

Ďalšou súčasťou frontendu je zobrazovanie grafov historických hodnôt
jednotlivých zariadení v~zobrazovanej schéme, ktoré sú uložené v~databázovom
systéme a~s~ktorou komunikuje pomocou jej programového rozhrania.

\section{Modbus multiplexer}

Niektoré Modbus zariadenia taktiež majú problém popísaný v~kapitole
\ref{chp:scada_protocols}, a~to že neumožňujú paralelný prístup viacerých
klientov, a~to aj v~prípade použitia Modbus/TCP.
Avšak k~zariadeniam potrebuje paralelne pristupovať viacero systémov, či už
systém zberu dát, vizualizačný systém alebo aj iné systémy a~aplikácie.
Preto je nevyhnuté vo~výslednom systéme vytvoriť spôsob multiplexovania týchto
Modbus zariadení a~serializovať prístupy k~nim bez nutnosti priamej koordinácie
jednotlivých pristupujúcich systémov.


\chapter{Výber použitých nástrojov, technológií a~knižníc}\label{chp:selection}

V~tejto kapitole uvediem nástroje, ktoré som vybral na~použitie vo~vlastnom
systéme a~taktiež dôvody ich výberu.

\section{InfluxDB}

Z~dostupných databázových systémov popísaných v~kapitole~\ref{chp:db_overview}
je najvhodnejším systém InfluxDB.

Jeho hlavnými výhodami oproti databázovému systému Prometheus sú plná podpora
\texten{push} modelu zbierania dát, čo umožní redukovať prevádzku na~sieti, aj
sériových linkách pripojených zariadení, ktoré podporujú oznámenia o~zmene
hodnoty.
Taktiež poskytuje lepšie webové rozhranie pre~zjednodušenú interakciu
s~databázovým systémom.

Oproti programu RRDTool zas ponúka oveľa viac možností, ktoré sú pre~výsledný
systém užitočné, a to lepšia organizácia dát a~ich variabilnejšie nastavenie
uchovávania, lepšie programové rozhranie pre získavanie dát z~databázového
systému a~dostupnosť natívneho systému zberu dát.

\section{Telegraf}

Zo~systémov zberu dát uvedených v~kapitole~\ref{chp:collection_systems} som
vybral nástroj Telegraf hneď z~niekoľkých dôvodov.

Tento nástroj je vyvíjaný spolu s~databázovým systémom InfluxDB jednou
spoločnosťou, a teda je zo~všetkých najkompatibilnejší a~dokáže dáta predávať
priamo vo~formáte \texten{InfluxDB Line Protocol}.
Taktiež je jeho výhodou podpora \texten{pull} modelu, podobne ako pri~vybranom
databázovom systéme.
V~neposlednom rade, tento nástroj umožňuje spracovanie a~agregovanie
zozbieraných dát z~viacerých zdrojov, čo ani jeden zo~zvyšných dvoch nástrojov
nepodporuje.

Telegraf má aj jednu nevýhodu, ktorú zdieľa s~ostatnými systémami zberu údajov,
a~to, že neobsahuje vstavaný plugin pre~protokol BACnet, a~tak je nutné takýto
plugin implementovať.

\section{Python}

Hlavným dôvodom, prečo som na~implementáciu vlastných nástrojov vybral
programovací jazyk Python je dostupnosť knižníc implementujúcich jednotlivé
komunikačné protokoly.
Menovite sa jedná o~protokol BACnet, kde jediné skriptovacie jazyky, pre~ktoré
existovala knižnica sú Python a~JavaScript.\;\cite{bacnet_libs}
Z~týchto dvoch som následne vybral Python.

Nevýhodou tohto programovacieho jazyka je, že jeho referenčná implementácia
CPython používa globálny zámok interpretu (\texten{Global Interpreter
Lock}).\;\cite{python_gil}
To efektívne umožňuje beh len jednému vláknu súčasne aj na~systémoch s~viacerými
jadrami procesora.

Mnou implementované nástroje neobsahujú mnoho výpočtovo náročných operácií, ale
hlavne operácie, ktoré čakajú na~komunikáciu, buď s~inými zariadeniami alebo
inými časťami vlastného systému.
Preto na~zefektívnenie mojich nástrojov využívam asynchrónne programovanie
pomocou knižnice Asyncio.

\subsection{Asyncio}

Asyncio je knižnica umožňujúca asynchrónne programovanie v~jazyku Python.
Je dostupná v~jeho štandardnej knižnici.
Základným prvkom sú korutiny, teda funkcie, ktoré keď čakajú napríklad
na~komunikáciu alebo výsledok inej korutiny, tak prenechajú priestor
na~vykonávanie inej bežiacej korutine, ktorá už má dostupný výsledok, na~ktorý
čakala.\;\cite{asyncio_coro}

Jadrom celej knižnice je nekonečný cyklus, ktorý čaká na~udalosti a~na~ich
základe ďalej spúšťa korutiny, ktoré môžu pokračovať
v~behu.\;\cite{asyncio_eventloop}

% Príklad behu programu, ktorý takto využíva asynchrónne programovanie je
% znázornený na~obrázku~\ref{fig:coroutines}, \verb|execute()| je korutina, ktorá
% odošle požiadavku do~cieľa v~sieti a~počká na~odpoveď.

% \begin{figure}[!h]
%     \centering
%     \begin{tikzpicture}
%         \node (coro_a) at (0, 0) {Korutina A};
%         \node[circle, draw=black] (coro_a_call) at (0, -1) {};
%         \node (coro_a_await) at (0, -2) {\verb|res = await exectue(req)|};
%         \draw [solid] (coro_a_call.south) -- (coro_a_await.north);
%         \node (coro_a_resume) at (0, -4) {(\verb|exectue(req)| vrátila výsledok)};
%         \draw [dashed] (coro_a_await.south) -- (coro_a_resume.north);
%         \node[circle, fill=black] (coro_a_end) at (0, -5) {};
%         \draw [solid] (coro_a_resume.south) -- (coro_a_end.north);

%         \node (coro_b) at (7, 0) {Korutina B};
%         \node[circle, draw=black] (coro_b_call) at (7, -2) {};
%         \node (coro_b_await) at (7, -3) {\verb|res = await exectue(req)|};
%         \draw [solid] (coro_b_call.south) -- (coro_b_await.north);
%         \node (coro_b_resume) at (7, -5) {(\verb|exectue(req)| vrátila výsledok)};
%         \draw [dashed] (coro_b_await.south) -- (coro_b_resume.north);
%         \node[circle, fill=black] (coro_b_end) at (7, -6) {};
%         \draw [solid] (coro_b_resume.south) -- (coro_b_end.north);
%     \end{tikzpicture}
%     \caption{Súbeh dvoch korutín}\label{fig:coroutines}
% \end{figure}

\section{Bacpypes}

Bacpypes\footnote{\url{https://github.com/JoelBender/bacpypes}} je knižnica
v~jazyku Python, ktorá implementuje BACnet/IP, teda protokol BACnet
nad~transportným protokolom UDP.

Na~vykonávanie akcií používa úlohy, teda triedy, ktoré dedia z~jedného z~typov
úloh.
Úlohy môžu byť buď jednorazové (dediace z~triedy \verb|OneShotTask|) alebo
opakujúce sa (dediace z~triedy \verb|RecurringTask|).
Pre zjednodušenie obsahuje knižnica aj dekorátory funkcií, ktoré pre~dekorovanú
funkciu automaticky vytvoria triedu úlohy.\;\cite{bacpypes_task}

Jadrom knižnice je trvalo bežiaci cyklus, ktorý pravidelne spúšťa definované
úlohy, ktoré by mali byť spustené a~taktiež odložené funkcie, napríklad
odoslanie paketu do siete.
Nevyžíva pritom knižnicu Asyncio, ale vlastný udalosťami riadený cyklus
s~pomocou knižnice Asyncore, ktorá, predchodcom
Asyncio.\;\cite{bacpypes_core_src}

\section{Pymodbus}

Knižnicou, ktorá do~jazyka Python pridáva podporu pre~protokol Modbus je
Pymodbus\footnote{\url{https://github.com/riptideio/pymodbus}}.
Dokáže fungovať ako klient, aj ako server.
V~oboch prípadoch vie komunikovať viacerými variantmi protokolu Modbus,
napríklad Modbus/TCP cez~sieť alebo Modbus/RTU po~sériovej linke v~binárnom
alebo ASCII formáte.\;\cite{pymodbus}

Jej výhodou je, že využíva knižnicu Asyncio\;\cite{pymodbus}, teda umožňuje
efektívne využitie času, kým sa čaká na~odpoveď druhej strany komunikácie, čo je
zvlášť výhodné v~prípade zariadení na~pomalšej sériovej linke.
Tento fakt bol aj jedným z~dôvodov jej výberu, ďalšími boli dobrá a~prehľadná
dokumentácia, ako aj pomerne jednoduché použitie.

\section{TOML}

TOML (\texten{Tom's Obvious, Minimal Language}) je jednoduchý formát určený
pre~konfiguračné súbory.
Podobne ako iné konfiguračné formáty, využíva páry kľúča a~hodnoty, ale
na~rozdiel od~iných umožňuje vytvoriť viacúrovňovú hierarchiu a~tiež polia
skalárnych alebo zložených typov.\;\cite{toml}

Tento formát som vybral pre~jednoduchosť jeho zápisu a~zároveň veľkú
vyjadrovaciu silu.
Taktiež je výhodou, že ho používajú aj iné vybrané nástroje, InfluxDB
a~Telegraf.

\subsection{Tomli}

S~výberom formátu konfigurácie súvisí aj výber knižnice na~spracovávanie týchto
konfiguračných súborov.
Pre Python existuje viacero knižníc, ja som vybral
Tomli\footnote{\url{https://github.com/hukkin/tomli}}, pretože od~verzie 3.11
jazyka Python je súčasťou štandardnej knižnice ako knižnica
Tomllib.\;\cite{tomli}
To umožní jej jednoduché nahradenie, a~tým aj minimalizáciu závislostí, bez
nutnosti úpravy kódu, ktorý túto knižnicu používa.

Pre jednoduchšie použitie som vytvoril nový dekorátor tried \verb|@configclass|,
ktorý rozširuje dekorátor \verb|@dataclass| nachádzajúci sa v~štandardnej
knižnici.
Ten umožňuje konverziu konfiguračného súboru priamo do~novej inštancie
dekorovanej triedy, ktorá má atribúty označené typovými anotáciami.
Tie moje rozšírenie používa pri~kontrole správnosti typov hodnôt
v~konfiguračnom súbore.
Dekorátor má aj jeden voliteľný parameter, a~to validačnú funkciu, ktorou sa dá
skontrolovať, že načítané hodnoty sú správne nie len typovo, ale aj sémanticky.

\section{FastAPI}

FastAPI\footnote{\url{https://fastapi.tiangolo.com/}} je webový framework
pre~Python.
Podľa nezávislého testovania spoločnosťou TechEmpower to je jeden
z~najrýchlejších dostupných frameworkov.\;\cite{fast_bench}
To je aj jeho veľkou výhodou, ktorá zavážila pri~výbere.

Taktiež využíva knižnicu Asyncio, čo umožňuje využitie asynchrónneho
programovania pri~komunikácii so zariadeniami v~technologických celkoch, tak aj
klientom vo~webovom prehliadači.
Medzi jeho ďalšie funkcie, ktoré boli dôležitými kritériami, patria podpora
protokolu WebSockets, čo je spojovaný protokol pre~obojsmernú komunikáciu
s~vytvorením spojenia cez HTTP a~podpora HTML šablón.

\section{Giraffe}

Na~rozdiel od~ostatných knižníc,
Giraffe\footnote{\url{https://github.com/influxdata/giraffe/}} je JavaScriptovou
knižnicou pre~framework React\footnote{\url{https://reactjs.org/}}.
Obsahuje React komponenty určené na~vykreslenie grafov dát vo~webovom
prehliadači.
Zabudovaných má niekoľko typov grafov, medzi ktoré patria aj čiarový a~bodový
graf.
Taktiež dokáže zobraziť dáta v~prehľadnej tabuľke.
Vykresľované grafy sú interaktívne a~používateľ ich dokáže posúvať a~ovplyvňovať
mierku na~osi X, čím ho priblíži, prípadne pri~prechode kurzorom zobraziť presnú
hodnotu v~momente, na~ktorý kurzor ukazuje.

Hlavným dôvodom výberu tejto konkrétnej knižnice je, že je použitá priamo
v~zabudovanom webovom rozhraní databázového systému InfluxDB.
Priamo tak podporuje rovnaký formát dát, ktorý je možné z~InfluxDB získať a~nie
je potrebné ich transformovať, knižnica ich sama spracuje a~zobrazí.


\chapter{Vlastné implementované nástroje}\label{chp:implementation}

V~nasledujúcej kapitole popíšem fungovanie mnou implementovaných nástrojov,
ktoré dopĺňajú už existujúce vybrané nástroje vo~vlastnom systéme.

\section{Modbus multiplexer}

Na~vyriešenie problému paralelného prístupu k~Modbus zariadeniam, implementujem
softvérový multiplexer protokolu Modbus -- Mbmux.

Samotný nástroj komunikuje s~ostatnými systémami prostredníctvom Modbus/TCP
servera implementovaného knižnicou Pymodbus.
Na~opačnej strane sú jednotlivé linky, ktoré Mbmux multiplexuje.
Linkou môže byť buď samotná sériová linka alebo zariadenie Modbus/TCP, ktoré
nepodporuje viacero súbežných pripojení.
Vo všeobecnosti teda ide o~cieľ, ku~ktorému je nutné jednotlivé prístupy
serializovať.
S~tými Mbmux komunikuje buď pomocou Modbus/TCP alebo Modbus/RTU klienta
z~knižnice Pymodbus.
Medzi týmito stranami presmerováva Mbmux požiadavky na~základe adresy zariadenia
v~prijatej požiadavke.
Túto adresu pred~poslaním voliteľne premapuje pomocou mapovacej tabuľky
definovanej pre~danú linku.

\subsection{Paralelný prístup}

Prístup k~jednej linke je po~prijatí Modbus/TCP spojenia serializovaný pomocou
zámku (\verb|Lock|) z~knižnice Asyncio.
Z~pohľadu ostatných systémov je tak prístup k~multiplexovaným zariadeniam
súbežný, keďže žiadosť o~pripojenie nie je odmietnutá.
Zároveň, prístupy k~rôznym linkám sa navzájom neovplyvňujú, preto má každá linka
svoj vlastný zámok.

Na~obrázku~\ref{fig:mbmux_accesses} sú znázornené tri paralelné spojenia
s~ostatnými systémami (\verb|cN|), pričom na~každom spojení dostal Mbmux
požiadavku (\verb|qN|).
Túto požiadavku presmeruje na~jednu z~dvoch liniek (\verb|lM|) a~odpoveď z~linky
(\verb|rN|) odošle naspäť, ako odpoveď inému systému.

\begin{figure}[!h]
    \centering
    \begin{tikzpicture}
        \node (accept_1) at (0, 0) {\verb|c1 = accept()|};
        \node (recv_1) at (0, -1) {\verb|q1 = c1.recv()|};
        \node (lock_1) at (0, -3) {\verb|l1.lock()|};
        \node (route_1) at (0, -4) {\verb|r1 = l1.send(q1)|};
        \node (unlock_1) at (0, -6) {\verb|l1.unlock()|};
        \node (send_1) at (0, -7) {\verb|c1.send(r1)|};
        \node (close_1) at (0, -9) {\verb|c1.close()|};
        \draw[solid] (accept_1) -- (recv_1) -- (lock_1);
        \draw[solid, double] (lock_1) -- (route_1) -- (unlock_1);
        \draw[solid] (unlock_1) -- (send_1) -- (close_1);

        \node (accept_2) at (4, 0) {\verb|c2 = accept()|};
        \node (recv_2) at (4, -1) {\verb|q2 = c2.recv()|};
        \node (lock_2) at (4, -3) {\verb|l1.lock()|};
        \node (locked_2) at (4, -6) {\verb|l1.lock()|};
        \node (route_2) at (4, -7) {\verb|r2 = l1.send(q2)|};
        \node (unlock_2) at (4, -9) {\verb|l1.unlock()|};
        \node (send_2) at (4, -10) {\verb|c2.send(r2)|};
        \node (close_2) at (4, -12) {\verb|c2.close()|};
        \draw[solid] (accept_2) -- (recv_2) -- (lock_2);
        \draw[dashed] (lock_2) -- (locked_2);
        \draw[solid, double] (locked_2) -- (route_2) -- (unlock_2);
        \draw[solid] (unlock_2) -- (send_2) -- (close_2);

        \node (accept_3) at (8, 0) {\verb|c3 = accept()|};
        \node (recv_3) at (8, -1) {\verb|q3 = c3.recv()|};
        \node (lock_3) at (8, -3) {\verb|l2.lock()|};
        \node (route_3) at (8, -4) {\verb|r3 = l2.send(q3)|};
        \node (unlock_3) at (8, -6) {\verb|l2.unlock()|};
        \node (send_3) at (8, -7) {\verb|c4.send(r3)|};
        \node (close_3) at (8, -9) {\verb|c3.close()|};
        \draw[solid] (accept_3) -- (recv_3) -- (lock_3);
        \draw[solid, double] (lock_3) -- (route_3) -- (unlock_3);
        \draw[solid] (unlock_3) -- (send_3) -- (close_3);
    \end{tikzpicture}
    \caption{Súbeh prístupov multiplexovaných nástrojom Mbmux (dvojitá čiara
    značí uzamknutý zámok, prerušovaná, čakanie na~uzamknutie)}%
    \label{fig:mbmux_accesses}
\end{figure}

\section{BACnet plugin pre~Telegraf}

Telegraf nemá vstavanú podporu pre~BACnet, ani preň neexistuje plugin tretej
strany, teda bolo potrebné túto podporu implementovať pomocou externého pluginu.
Využívam na~to \texten{execd} plugin nástroja Telegraf, ktorý pri~spustení
spustí externý program a~číta dáta zo~štandardného výstupu externého programu.
Samotný plugin podporuje viacero spôsobov konfigurácie sledovaných zariadení
a~taktiež viacero spôsobov zbierania samotných dát.

\subsection{Konfigurácia sledovaných zariadení}

Prvým spôsobom je manuálna konfigurácia všetkých zariadení, ktoré má plugin
sledovať.
Pri~každom je potrebné špecifikovať adresu zariadenia, jednotlivé objekty
a~vlastnosti objektov, ktoré má plugin sledovať.

Druhým spôsobom je automatické vyhľadanie všetkých zariadení v~sieti, čo plugin
dosiahne počúvaním na~odpovede na~\verb|WhoIs| požiadavku smerovanú všetkým
zariadeniam v~sieti (\texten{broadcast}).
Tú dokáže voliteľne vysielať opakovane, aby tak dokázal reagovať na~zmeny
v~sieti.
Následne po~prijatí odpovede ďalšími požiadavkami smerovanými na~objavené
zariadenie zistí, aké objekty má.

Oba spôsoby je možné kombinovať, manuálne nakonfigurované zariadenia sú pri~ich
znovuobjavení ignorované.

\subsection{Zbieranie dát zo~zariadení}

Prvou možnosťou je čítanie v~pravidelných intervaloch.
Plugin číta všetky, či už manuálne definované alebo objavené objekty pomocou
niekoľkých \verb|ReadProperty| požiadaviek alebo ak to zariadenie podporuje, tak
pomocou jednej \verb|ReadPropertyMultiple| požiadavky.
Zároveň na~rozloženie prevádzky v~sieti sú čítania jednotlivých zariadení
a~objektov na~začiatku posunuté o~náhodnú hodnotu (Obr.~\ref{fig:tb_offsets}).

\begin{figure}[!h]
    \centering
    \begin{tikzpicture}
        \node (dev1) at (-1.5, 0) {Objekt 1};
        \draw (0, 0)
            -- (1, 0) node[fill=black, circle]{}
            -- (5, 0) node[fill=black, circle]{}
            -- (9, 0) node[fill=black, circle]{}
            -- (11, 0);
        \node (dev2) at (-1.5, -1) {Objekt 2};
        \draw (0, -1)
            -- (0, -1) node[fill=black, circle]{}
            -- (4, -1) node[fill=black, circle]{}
            -- (8, -1) node[fill=black, circle]{}
            -- (11, -1);
        \node (dev3) at (-1.5, -2) {Objekt 3};
        \draw (0, -2)
            -- (3, -2) node[fill=black, circle]{}
            -- (7, -2) node[fill=black, circle]{}
            -- (11, -2) node[fill=black, circle]{};
    \end{tikzpicture}
    \caption{Časový priebeh čítaní jednotlivých objektov (bod označuje moment
    čítania)}%
    \label{fig:tb_offsets}
\end{figure}

Druhou možnosťou je využitie oznámení o~zmene hodnoty, ktoré protokol BACnet
ponúka.
Plugin na~objavené alebo definované zariadenie odošle \verb|SubscribeCOV|
požiadavku pre~každý objekt, ktorý chce monitorovať a~zariadenie potom samé
odošle pluginu informáciu o~zmene hodnoty niektorej vlastnosti sledovaného
objektu.
Plugin túto požiadavku posiela opakovane podľa nastavenej životnosti požiadavky,
aby pri~opakovanom spustení pluginu nezostávali na~sledovaných zariadeniach
nevyužité požiadavky príliš dlho.

Aj tieto dva spôsoby je možné kombinovať, v~závislosti od~možností sledovaných
zariadení.

\section{Vizualizácia a~ovládanie}

Posledným implementovaným je nástroj slúžiaci na~samotnú vizualizáciu
a~ovládanie technologických celkov a~manuálne nastavenie hodnôt zariadení sa
v~ňom nachádzajúcich.

\subsection{Backend}

Backend tvorí trvalo bežiaci webový server.
Ten po~prijatí HTTP požiadavky na~stránku s~konkrétnou schémou získa pomocou
modulov všetky potrebné dáta z~technologických celkov a~na~ich základe upraví
definovanú SVG schému technologického celku podľa nakonfigurovaných pravidiel,
napríklad vloží nameranú hodnotu ako text elementu alebo podľa hodnoty rozhodne
o~zmene niektorého alebo všetkých štýlov definovaného elementu.
Celú HTML stránku aj s~vloženou SVG schémou nakoniec odošle ako odpoveď naspäť
klientovi.
Táto úprava SVG ešte na~serveri zaručí, že užívateľ hneď po~načítaní stránky
vidí aktuálne dáta a~nemusí ešte znovu čakať na~ich načítanie.

\subsection{Systém dátových modulov}

Backend na~načítavanie dát pomocou rôznych protokolov využíva systém dátových
modulov.
To umožňuje jednoduché rozšírenie nástroja o~podporu ďalších protokolov.

Dátový modul je trieda dediaca z~triedy \verb|DataModule| a~implementujúca jej
dve metódy \verb|get_value()| a \verb|set_value()|, ktoré nie sú v~základnej
triede implementované.
Voliteľne môže implementovať ešte ďalšie metódy, podľa možností daného
protokolu, konkrétne na~čítanie a~zapisovanie viacerých hodnôt naraz
a~na~podporu oznámení o~zmene sledovanej hodnoty.

\begin{lstlisting}[
    captionpos=b,
    caption=Príklad minimálneho dátového modulu,
    language=Python
]
class RandomDataModule(DataModule):
    name = "random"

    async def get_value(self, data_id: str) \
            -> str | list[str]:
        return str(random())

    async def set_value(self, data_id: str, value: str) \
            -> str | list[str] | None:
        return value
\end{lstlisting}

Zdroj dát je v~schéme definovaný názvom dátového modulu, z~ktorého majú byť dáta
získané a~identifikačným reťazcom, ktorý je pri~operáciách predaný dátovému
modulu.
Ten na~základe tohto identifikačného reťazca rozhodne, odkiaľ budú dáta získané.
Môže tak obsahovať priamo adresu konkrétneho zariadenia alebo iný identifikátor,
podľa implementácie daného dátového modulu.

Aktuálne sú podporované protokoly BACnet, Modbus a~SNMP.
Ďalej je zahrnutý aj dátový modul generujúci náhodné hodnoty, ktorý môže slúžiť
na~testovacie účely.

\subsection{Frontend}

Frontend nástroja na~hlavnej stránke zobrazí zoznam schém, ktoré sú
nakonfigurované v~backedne ako odkazy na~stránku schémy.
Na~stránke schémy sa zobrazuje samotná schéma technologického celku ako vložené
SVG (Obr.~\ref{fig:visu_scheme}).

\begin{figure}[!h]
    \centering
    \includegraphics[width=14cm]{./Images/Implementation/visu_scheme.png}
    \caption{Zobrazenie schémy vo~vizualizačnom nástroji}%
    \label{fig:visu_scheme}
\end{figure}

V~prípade, že je JavaScript vo~webovom prehliadači povolený, tak sa frontend
pripojí na~backend pomocou protokolu WebSockets.
Cez toto spojenie následne komunikuje s~backendom a~získava aktuálne hodnoty,
podľa ktorých aktualizuje elementy v~SVG.
Tým, že WebSockets umožňuje obojsmernú komunikáciu, tak po~načítaní požiada
server o~zasielanie oznámení o~zmene hodnoty v~prípade, že to dátový modul
podporuje a~nie je to zakázané v~konfigurácii schémy.
Ak takéto oznámenia dátový modul nepodporuje, tak v~pravidelných intervaloch
posiela backendu požiadavky na~čítanie dát.

Taktiež po~kliknutí na~prvok v~SVG zobrazí modálne okno s~interaktívnym grafom
zobrazujúcim historické hodnoty daného prvku uložené v~databáze InfluxDB
(Obr.~\ref{fig:visu_modal}).
Tieto dáta sú tiež získané z~backendu, aby sa frontend nemusel pripájať priamo
na~databázu.
V~rovnakom okne je taktiež možné manuálne nastaviť hodnotu prvku, pokiaľ to je
konfiguráciou schémy dovolené.

\begin{figure}[!h]
    \centering
    \includegraphics[width=14cm]{./Images/Implementation/visu_modal.png}
    \caption{Modálne okno s~grafom historických hodnôt vo~vizualizačnom
    nástroji}%
    \label{fig:visu_modal}
\end{figure}
