Perl (69) - Projekt - online přenos

Perl Dnes dokončíme projekt, jehož obsahem bylo vytvořit uživatelsky přívětivou čtečku sportovních výsledků pro příkazový řádek.

6.6.2008 07:00 | Jiří Václavík | přečteno 11146×

Dnes máme za úkol dokončit práce na projektu. Chybí nám napsat poslední věc a tou je online přenos.

Online přenos

Načrtněme si opět předběžný postup.

Pojďme se do toho pustit. Bude třeba napsat tuto podmínku, kterou jsme nechali minule neimplementovanou.

if($online){
    #implementace online přenosu
}

Předně musíme ze všech zápasů vybrat pouze jediný. K tomu si napíšeme zvlášť podprogram.

    @vyhovujici = vyber_zapas(@vyhovujici);

To je v podstatě opakované volání metody najdi_zapas_podle_kriterii, která na základě kritérií postupně zužuje výběr. Jakmile klesne na jediný zápas, můžeme výběr ukončit a postupovat dále.

sub vyber_zapas {
    my @vyhovujici = @_;
    while(scalar @vyhovujici > 1){
        print "Byly nalezeny zapasy (".scalar @vyhovujici."):\n";
        tiskni_vyhovujici(@vyhovujici);
        print "Musite zadat zpresnujici kriterium: ";
        my $kriteria;
        chomp($kriteria = <STDIN>);
        my @kriteria = split(" ", $kriteria);
        @vsechna_kriteria = (@vsechna_kriteria, @kriteria);
        @vyhovujici = $live->najdi_zapas_podle_kriterii(\@vyhovujici, \@kriteria);
    }
    return @vyhovujici;
}

Je ovšem možné, že doplňující kritérium vyloučí všechny zápasy. V takovém případě musíme program ukončit, protože nemáme jak pokračovat. Jinou možností by bylo nebrat poslední kritérium v úvahu.

   die "Zadny vyhovujici zapas\n" if scalar @vyhovujici == 0;

Nyní zjistíme ligu a číslo zápasu, které je třeba předat metodě prenos. Ale je třeba si dát pozor na tuto věc: Číslo zápasu a liga (parametry odkaz_idzapasu a odkaz_liga v hashi s informacemi o zápase) nemusí být vůbec dostupné - resp. nejsou dostupné, pokud zatím v zápase nedošlo ke sledované události. Nastává zde tedy problém. Jak můžeme získat data, když nevíme odkud? Tuto otázku nemá smysl řešit - tato data nezískáme, protože neexistují.

Vyřešíme to trochu jinak a to tak, že si počkáme, až budou dostupná. To znamená průběžně kontrolovat, zda nenastala událost a neobjevil se nám odkaz na seznam událostí. Jakmile událost nastane, získáme odkaz a budeme ji moci zobrazit. Bude to sice o něco náročnější na programování, ale v zásadě tím nic neztrácíme.

Periodicky tak budeme stahovat informace o zápasech. Vždy v nich najdeme ten náš zápas, který sledujeme. Ten nyní může být ve čtyřech stavech.

Je-li zápas před výkopem, budeme v intervalech zjišťovat, zda to stále platí, dokud se nezačne hrát. Když se začne hrát, nejsou zpravidla dostupné žádné události. Proto budeme v intervalech tisknout pouze probíhající minutu. Jakmile ale dojde k události, vyskočíme z cyklu, protože v tomto okamžiku budeme schopni stáhnout podrobnosti k zápasu - již známe odkaz (prvky odkaz_idzapasu a odkaz_liga). Pokud zápas skončil, ukončíme cyklus také. Pokud tyto požadavky zahrneme do další části programu, získáme toto.

Nejprve tedy zkusíme, zda již jsou detaily dostupné.

    my $vyhovujici_liga = $vyhovujici[0]{"odkaz_liga"};
    my $vyhovujici_idzapasu = $vyhovujici[0]{"odkaz_idzapasu"};
    my $probihajici_minuta;

Pokud ne, budeme si na ně muset počkat. Periodicky budeme kontrolovat, zda již jsou dostupné.

    while(!$vyhovujici_idzapasu){
        @vyhovujici = $live->ziskej_zapasy_dane_ligy();
        @vyhovujici = $live->najdi_zapas_podle_kriterii(\@vyhovujici, \@vsechna_kriteria);

        $probihajici_minuta = $vyhovujici[0]{"minuta"}, "\n";
        last if($vyhovujici[0]{"hraje_se"} == Livescore::UKONCEN);
        if($vyhovujici[0]{"odkaz_idzapasu"}){
            $vyhovujici_idzapasu = $vyhovujici[0]{"odkaz_idzapasu"};
            $vyhovujici_liga = $vyhovujici[0]{"liga"};
        }

        if($vyhovujici[0]{"hraje_se"} == Livescore::PROBIHA){
            print "Probiha $probihajici_minuta'          \r";
        }elsif($vyhovujici[0]{"hraje_se"} == Livescore::PRED_VYKOPEM){
            print "Zapas zatim nezacal\r";
        }
        sleep $refresh;
    }

K tomu, aby se nám neuchovávala data ve výstupním bufferu zatímco je v činnosti funkce sleep je třeba připsat na začátek skriptu tyto dva řádky. Vyprazdňování bufferu musí být automatické.

use IO::Handle;
STDOUT->autoflush(1);

Pokud skončil předchozí cyklus, znamená to, že buď zápas skončil nebo probíhá a jsou dostupné události. V obou případech vytiskneme dostupné události. Jestliže zápas probíhá, budeme navíc tisknout přibyvší události dokud zápas také neskončí.

    do {
        my @udalosti = $live->prenos($vyhovujici_liga, $vyhovujici_idzapasu);
        $probihajici_minuta = shift @udalosti;
        $hraje_se = shift @udalosti;

        #vytiskneme nové události

        print "Probiha $probihajici_minuta'\r";
        sleep $refresh if $hraje_se == Livescore::PROBIHA;
    }while($hraje_se == Livescore::PROBIHA);

Nyní projdeme všechny události a ty, které jsou nové zobrazíme. ID starých událostí budeme kopírovat do pole @zobrazene_udalosti, abychom měli přehled o tom, co již bylo zobrazeno.

        for (@{$udalosti[0]}){
            next if in_array($_->{"id_udalosti"}, \@zobrazene_udalosti);

            #zobrazíme událost a zapíšeme do @zobrazene_udalosti

        }

Nyní zbývají poslední dva kroky. Zvolíme vhodnou formu zobrazení a opět pomocí funkce colored vytiskneme. Máme 3 druhy události - gól, žlutá karta a červená karta. Na základě prvku udalost můžeme u všech událostí rozhodnout, o který případ jde. Poté aktualizujeme pole @zobrazene_udalosti.

            if ($_->{"udalost"} eq "yellow"){
                print colored(sprintf("%2s'     %-20s (%s)\a\n", $_->{"minuta"},
                    $_->{"hrac"}, $_->{"tym"}), "bold yellow");
            }elsif ($_->{"udalost"} eq "red" or $5 eq "yellow-red"){
                print colored(sprintf("%2s'     %-20s (%s)\a\n", $_->{"minuta"},
                    $_->{"hrac"}, $_->{"tym"}), "bold red");
            }elsif ($_->{"udalost"} eq "goal"){
                print colored(sprintf("%2s' ", $_->{"minuta"}), "bold white");
                print colored(sprintf("%d:%d ", $_->{"skore1"}, $_->{"skore2"}),
                    "bold yellow");
                print colored(sprintf("%-20s (%s)\a\n", $_->{"hrac"}, $_->{"tym"}),
                    "bold white");
            }
            push @zobrazene_udalosti, $_->{"id_udalosti"};
        }

Za cyklus do-while se program dostane pouze v jediném případě - pokud skončil zápas. Tuto informaci tedy vytiskneme a na závěr zopakujeme aktualizovaný výsledek.

    print "Zapas skoncil\n";
    $live->ziskej_zapasy_dane_ligy;
    tiskni_vyhovujici($live->najdi_zapas_podle_kriterii(\@vyhovujici, \@vsechna_kriteria));
    exit;

Závěr

Nyní jsme hotovi. Všechny požadavky ze zadání projektu jsme úspěšně dokončili. Kompletní program si můžete stáhnout (Livescore.pm, live) a vyzkoušet.

live -o -l italy torino live -o li + interaktivní zadání live -o feyenoord

Výsledek zápasu *** Výsledek interaktivně vybraného zápasu *** Aktualizovaný probíhající online přenos

Samozřejmě by nyní tento program mohl být rozšířen o nové funkce. Například souběžné online přenosy více zápasů najednou, lepší členění pomocí přepínače -l a s tím související vylepšení cachování nebo přepis do Curses, Qt či jiného pokročilejšího uživatelského rozhraní.

Nicméně náš původní cíl je nyní splněn a proto tato šestidílná série, jejíž cílem bylo předvést některé techniky probrané v seriálu i z trochu jiného pohledu, dneškem končí.

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1544