site logo

Monitorování stavu zařízení pomocí AJAXu

Článek není výukový kurz technologie AJAX, je to živá praktická ukázka použití technologie AJAX na intranetu výrobní firmy. V textu najdete ukázky kódu a poznámky s upozorněním na některá úskalí této technologie. Ke studiu do hloubky doporučuji sérii článků Mastering Ajax na serveru IBM developerworks.

Příklad je z intranetové aplikace tavení skla. Nad schématický obrázek sklářské vany jsou umístěny buňky (rámečky) pro zobrazení reálných procesních hodnot. Po načtení stránky se začnou přenášet a aktualizovat pouze reálná data (zde v intervalu 5 sec.), všechny ostatní prvky stránky zůstávají beze změny. Živá (reálná, okamžitá) data jsou uložena v databázi na serveru, tam je zapisuje řídící systém tavícího agregátu.

Základ ukázky pochází z roku 2001, v ukázce chybí původní DHTML popisy jednotlivých měřících míst a linky na stránky pro zobrazení trendů. Reálný čas odpovídá skutečnému času serveru, hodnoty měřených veličin zde nejsou z databáze, ale jsou vytvořeny pomocí generátoru náhodných čísel. Počet měřících míst je jen 4 oproti původním 16.

real time:

Podobně vypadá úvodní stránka vizualizační části řídícího systému tavení. I tato intranetová stránka může být zobrazena v prohlížeči několik hodin a stále zobrazuje reálná data. Pak stačí letný pohled na obrazovku pro zjištění, zda parametry výrobního procesu jsou v pořádku. Webové rozhraní aplikace nenabízí tak vysoký komfort jako řídící systém, ale nepotřebuje drahé licence, hardwarové klíče ani instalaci software. Vhodné pro občasnou kontrolu stavu tavení.

Kód CSS

Nejdříve několik poznámek ke kódu CSS. Buňky, pro zobrazení naměřených reálných hodnot využívají třídu Lb. V sekci CSS uvedeme jen definici základního vzhledu a rozměrů prvku. Pozice (umístění) jednotlivých buněk jsou určeny až v inline stylu.


<style type="text/css">
.Lb {font: 13px Arial, SansSerif, Verdana; text-align: center;
     position: absolute; width: 45px; height: 15px;
     border: 1px solid #ccc; background: #fff; color: #006;}
</style>

<body>
...
<div style="position: relative; width: 580px; height: 326px;">
<img src="tank.png" style="width: 580px; height: 326px;">

<div class="Lb" style="top:35px; left: 216px; width: 60px;
    border: 0; background: transparent;">real time:</div>
<div id="d0" class="Lb" style="top:  35px; left: 280px; width: 132px;"></div>
<div id="d1" class="Lb" style="top: 131px; left: 193px;"></div>
<div id="d2" class="Lb" style="top: 171px; left: 257px;"></div>
<div id="d3" class="Lb" style="top: 262px; left: 190px;"></div>
<div id="d4" class="Lb" style="top: 262px; left: 290px;"></div>
</div>
...
</body>

Schématický obrázek zařízení a buňky měřících bodů jsou umístěny do prvku <div>, který tvoří konteiner pro tyto prvky. Konteiner má pozici relative abychom mohli uvnitř tohoto konteineru umístit buňky měřících bodů do žádaného místa pomocí pozicování absolute, top a left. Umístění buněk (rámečků) jednotlivých měřících bodů odpovídá přibližně umístění senzorů. Každá buňka má přiděleno id podle pořadí jeho hodnoty v Ajax datovém souboru.

Skript v browseru

Ladění Ajaxových skriptů je o poznání obtížnější než ladění skriptů pro DHTML, nebo pro validaci formulářů. Dílčí části skriptu běží na různých počítačích (klient-browser / server) a posílají si zprávy přes síť. První část skriptu vytvoří a pošle požadavek (běží v browseru), druhá část přijme požadavek, vytvoří a odešle odpověď (běží na serveru) a třetí část skriptu příjme a vyhodnotí odpověď (běží opět v browseru). Pak mohou nastat problémy při lokalizaci případné chyby.

Většina výukových článků používá podobný kód i s podrobným vysvětlením, proto uvedu jen nezbytný popis. Přenos dat je zajištěn pomocí objektu XMLHttpRequest, který může přenášet buď  XML data, nebo prostý text. V tomto příkladu je přenášen prostý text. Celý přenos dat zajišťují dvě funkce: Next() provede vytvoření a inicializaci objektu req, nastaví funkci pro zpracování odpovědi, odešle požadavek a nastaví timer pro opakování přenosu. Funkce processReq() testuje stav odpovědi, došlou odpověď  zpracuje a vypíše přijatá data do příslušných buněk. Zbývá jen spustit celý proces, to provede poslední příkaz (řádek 30) po načtení celé stránky.


 1. <script type="text/javascript">
 2. var req, URL = "getStatus.asp";
 3. function processReq() {
 4.   if(req.readyState == 4) {
 5.     if(req.status == 200) {
 6.       var data = "";
 7.       data = req.responseText.split("|");
 8.       for(i=0; i<5; i++) {
 9.         document.getElementById("d" + (i)).innerHTML = data[i];
10.       }
11.     }
12.   }
13. }
14.
15. function Next() {
16.   delete req;
17.   if (window.XMLHttpRequest) {     // IE7, FF, Opera, ...
18.     req = new XMLHttpRequest();
19.   }
20.   else if (window.ActiveXObject) { // <=IE6
21.     req = new ActiveXObject("Microsoft.XMLHTTP");
22.   }
23.   if(req) {
24.     req.onreadystatechange = processReq;
25.     req.open("GET", URL, true);
26.     req.send(null);
27.     window.setTimeout("Next()", 5000);
28.   } 
29. }
30. window.onload = Next;
31. </script>

Zpracování přijatých dat zajistí řádky 6-10. Nejdříve se datová řádka rozloží na jednotlivé hodnoty (řádek 7), potom se v cyklu (řádky 8, 9, 10) hodnoty zapíší do příslušných buněk měřících míst. Jako oddělovač dat je zde použit znak | (pipe) ve shodě s tím jako jsou data připravena na serveru. Pro zápis do buňky je použito id označení buňky, (např. id="d4") a funkce document.getElementById("d" + (i)). Vlastnost innerHTML není standardizovaná, ale umí ji všechny běžné browsery.

Skript záměrně neobsahuje odchytávání chybových stavů. Stránka buďto funguje, nebo ne. Pokud uživatel nemá možnost zjednat nápravu, nemá význam zobrazovat chybové zprávy.

Popsaný příklad nepředává serveru žádné parametry, v tomto případě to není potřeba, server má za úkol zaslat zpět aktuální data. Pokud potřebujeme předat serveru nějaká data (parametry), pak využijeme metody GET a query stringu, tedy za adresu serverového skriptu přidáme otazník a parametr např. getRecord.asp?id=1234. Delší seznam parametrů můžeme předat metodou POST (řádek 25) a vložením seznamu do metody send v řádku 26. Parametry jsou zapisovány jako páry hodnot jméno=hodnota, každý pár je oddělen znakem &, tedy stejně jako při odesílání formulářů, speciální znaky v hodnotách musí být zakódovány do escape sekvence.

Skript na serveru

Serverový skript je vhodné odladit samostatně, nezávisle na kódu browseru. Zadejte adresu serverového skriptu AJAXu do adresního řádku browseru (včetně případných parametrů), tak získáte viditelná data, která posílá server a zkontrolujte zdrojový kód stránky. Následující ukázka je výpis kódu z našeho příkladu. Všimněte si, že serverový skript nedoplňuje do zprávy žádné sekce typu html, head, nebo body. Zasílán je prostý text, jednotlivé položky jsou odděleny znakem pipe.

27.3.2007 21:14:30|1351|1446|361.2|721.6

Vyrovnávací paměť cache a kódová stránka

Podle nastavené doby platnosti zprávy si prohlížeč ukládá přečtená data do vyrovnávací paměti a při dalším dotazu, pokud jsou data stále platná, již nečte data ze serveru, ale z paměti cache browseru. Může nastat případ, kdy browser používá stále stejná data z cache. Potřebujeme nastavit expiraci zprávy tak, aby platnost vypršela okamžitě.

Jiný problém je kódová stránka. Původní, zobrazená HTML stránka i Ajaxová zpráva musí používat shodnou znakovou sadu, kódovou stránku. Ajax defaultně používá kódovou stránku UTF-8 a ne vždy máme možnost tuto znakovou sadu použít. Pokud posíláme text v jiné kódové stránce bude zpráva vypadat podobně jako v následujícím screenshotu.

AJAX: špatná znaková sada

Používat na webových stránkách kódování UTF-8 je žádoucí a správné, ale ne vždy je to možné a snadné. Například když databázi využívají webové i desktopové programy. Znaky s diakritikou v kódu UTF-8 zabírají více bytů a to zásadně ovlivní práci s textovými řetězci.

Protože Ajaxová zpráva neobsahuje sekci head, nemůžeme použít meta tágy pro nastavení expirace, nebo kódové stránky a musíme modifikovat hlavičku protokolu HTTP. Modifikace hlavičky HTTP musí provedena ještě před výstupem kódu HTML. Kód pro modifikaci je uveden v následující ukázce. Připomínám, že HTTP hlavička není vidět ve zdrojovém kódu stránky.

ASP:
<%
Response.AddHeader("Cache-Control", "no-cache")
Response.AddHeader("Content-Type: text/html", "charset=Windows-1250")
%>

PHP:
<?PHP
header("Cache-Control: no-cache");
header("Content-type: text/html; charset=Windows-1250") 
?>

Dobu expirace můžeme nastavit i přímo pro Ajaxový skript na serveru, podle článku IIS základní nastavení. Jiný postup jak si vynutit nová data je vytvořit URL vždy s novým, unikátním parametrem. Jako vhodný parametr může posloužit časový údaj (13 místné číslo udávající počet milisekund někdy od roku 1970). Za řádek 24 vložíme například následující příkaz: URL = "getStatus.asp?t=" + ((new Date()).valueOf());

Nastavení znakové sady pomocí hlavičky HTTP funguje ve všech standardních prohlížečích kromě Opery 9.6, ta předpokládá Ajaxová data jen ve formátu UTF-8.

Ajax omezení

Pomocí objektu XMLHttpRequest v browseru můžeme přenášet data jen z toho serveru, ze kterého pochází původní webová stránka, nebo ze serveru, který je ve stejné doméně. Prohlížeč tedy nemůže žádat data z libovolné domény. To může dělat jen server. Jak na to popisuje článek Ajax na serveru.

V browseru používáme vždy asynchronní přenos dat. Synchronní přenos totiž pozastaví browser do doby, než přijde odpověď ze serveru, během této doby prohlížeč přestane reagovat. Synchronní přenos je vhodný pro Ajax na serveru.

Pokud provádíte vývoj na pracovní stanici pomocí IIS, počítejte s limitem připojených uživatelů. Maximálně jde ladit Ajax kód ve dvou současně spuštěných browserech (stránka s Ajaxem jsou dva uživatelé).

updated 24.07.2009