portal Michała Hanćkowiaka
Begin main content
Search · Index
No registered users in community Materiały
in last 10 minutes

Tutorial do symulatora (ALR31S)

 
# Instalacja symulatora:
#
# 1. rozpakowac plik symul.zip
# 2. uruchomic: ./tclkit konsola2c.tcl
#    lub: ./tclkit konsola2c.tcl moj_skrypt.tcl
#    uwaga: symulator wymaga tcl v8.4 lub v8.6 (NIE v8.5 !)
# 3. do konsoli wkleic tekst zrodlowy poprzez schowek
#    klawisze shift+insert (copy), ctrl+insert (paste)
#    lub wpisac go recznie;
#    dzialaja tez klawisze do wklejania lub zapisywania plików: 
#    Ctrl+O/S/Z - Open/Save/Undo
# 4. uruchamiac fragmenty tekstu zrodlowego:
#    zaznaczyc fragment + ctrl_enter
#    lub ustawic kursor na linii + ctrl_enter (uruchamia te linie)
#
 
 
# Uwagi ogolne na temat sumulatora:
#
# 1. wierzcholki sieci są odizolowane od siebie...
#      sa interpreterami logicznymi i fiberami 
#      (fiber to inaczej korutina lub "wątek bez wywłaszczania")
#
# 2. komunikacja wierzcholka "v" z sasiadami:
#    + wierzchołek posiada następujące zmienne (zm. globalne j. Tcl):
#      id, id_los, liczbaWierz, stopien, kom(0), kom(1), ... 
#        "kom" to tablica (asocjacyjna) j. Tcl
#    + zmienna kom(X) zawiera listę komunikatow wysłanych w poprzedniej rundzie 
#      przez sąsiada wierzchołka "v", 
#      poprzez połączenie nr X (nr X z pkt. widzenia "v")
#        komunikaty z połączenia $i odczytujemy przez $kom($i) w j. Tcl;
#        istnieje też komenda, ktora zdejmuje pierwszy komunikat z kom(X):
#          czytaj X
#          [np. set msg [czytaj $i]]
#    + komunikaty wysylamy przez połączenie X przy pomocy 
#        wyslij X $msg
#        [np. wyslij 0 "ABC"; wyslij $i $msg]
#
# 3. co jeszcze posiada wierzchołek:
#    + "id_los" to duza liczba wybrana losowo (czyli moga sie powtorzyc!)
#    + "stopien" to stopien wierzchołka (=liczba sąsiadów)
#    + komenda "_puts {tekst}" wysyla napisy do okienka output
#
# 3a. przestarzałe zm./proc., które posiada wierzchołek:
#    + kom0, kom1, ... zamiast kom(0), kom(1), ...
#    + "komX_wyslij msg" zamiast "wyslij X msg"
#    + ... o czym zapomniałem???

# 4. symulacja modelu synchronicznego
#    + tworzymy tyle fiberow ile jest wierz. sieci
#      oraz definiujemy program główny:
#        fiber create $liczbaWierz { 
#          # tutaj program główny
#        }
#      UWAGA: warto uzywac procedur, a nie tylko glownego programu!
#        proc trzeba definiowac we wszystkich fiberach przy pomocy
#          fiber_iterate { ... def proc ... }
#    + koniec rundy oznaczamy w kodzie algorytmu przez
#        fiber yield
#    + uruchamiamy pojedyncze rundy z konsoli, przy pomocy
#        fiber yield; runda
 
# 5. symulacja modelu asynchronicznego:
#    + tworzymy tyle fiberow ile jest wierz. sieci
#      oraz definiujemy glowny program
#    + generujemy "zdarzenie obliczeniowe" z konsoli przy pomocy
#        fiber switchto $id
#      a zdarzenie to kończy się w kodzie algorytmu przez
#        fiber switchto main
#    + generujemy "zdarzenia dostarczenia komunikatu" z konsoli przy pomocy
#        dostarczKom $id $i
#      gdzie $i to nr połączenia wierzchołka $id
#    + istnieje procedura "dostarcz" działająca tak jak "wyslij"
#      z tą różnicą, że komunikaty są od razu dostarczane 
#      (nie ma potrzeby generowania zdarzenia dostarczenia przez "dostarczKom")


# Debugger/uwagi:
#
# 1. "fiber error" wyswietla stan fiberow;
#      {} -> fiber dziala
#      ended -> fiber sie zakonczyl w naturalny sposob
#      {... opis bledu ...} -> fiber sie zakonczyl z powodu bledu
#    otrzymujemy liste, po 1 elem dla kazdego fibera
#    uwaga: każdy fiber ma zm globalną "errorInfo", która zawiera dokładny opis błędu 
#      odczyt: "fiber_eval ID {set errorInfo}", gdzie ID to id fibera z błędem
#
# 2. zapetlenie sie 1 fibera powoduje zapetlenie calego symulatora
#      trzeba wtedy "wykilowac" symulator
#
# 3. aby spowodowac zakonczenie sie fibera, trzeba sprowokowac blad;
#      np. jesli mamy petle 
#          while {$::run} { # $run dotyczy zm. lokalnej, $::run - zm. globalnej
#             ...
#             ...
#             fiber yield
#          }
#      to jesli usuniemy zmienna $run (globalna) to wystapi blad i fiber sie zakonczy!!
#      mozna to zrobic gdy fiber jest nieczynny np. tak:
#        fiber_eval ID {unset run}; # ID to id fibera/wierzcholka
#        set_run qq; # wyrażenie z "qq" jest zawsze błędne
#
# 4. jesli wszystkie fibery sie zakonczyly (naturalnie lub z powodu bledu)
#      to mozna usunac fibery (przez "fiber delete") i znów je stworzyć
#
# 5. jak skutecznie "restartować fibery" ?
#      trzeba zmusic WSZYSTKIE fibery zeby zakończyły główny program
#      np. jeśli w warunku pętli jest zm. "run" to wystarczy zrobic:
#      fiber_iterate {unset run}; fiber yield
#        # wszystkie fibery powinny się zakończyć z powodu błędu "braku zm. run"
#        # upewnić się przy pomocy "fiber error" ...
#      potem wykonać "fiber restart"
#    przydatna procedura unset_run podobna do set_run, 10.2018:
#      proc unset_run {} {fiber_iterate {unset ::run}}
#        # proc ta usuwa zmienne globalne run w fiberach!
#
# 6. debugger krokowy, nowość 04.2019 (potrzebny nowy symul.zip) !!!
#      w ten sposób:
#        fiber_dbg_proc 0 run
#      instaluje się graficzny debugger dla fibera z id=0 i procedury "run"
#      !!! UWAGA !!!
#      kolejną rundę uruchamiać dopiero gdy mamy PEWNOŚĆ
#      że poprzednia runda się zakończyła (inaczej nastąpi crash...)
#


# Uwagi dodatkowe:
#
# 1. mozna uzywac pomocniczych komend fiberowych
#      fiber_eval $id $kod
#        wykonuje kod w fiberze $id (na poziomie globalnym)
#        np:
#          fiber_eval 0 {set x 123}
#            # fiber z id==0 bedzie mial zm. globalna x
#      fiber_iterate $kod
#        iteruje po wszystkich fiberach i wykonuje kod (na poz. globalnym)
#        np:
#          fiber_iterate { proc qqq p {_puts $p} }
#            # wszystkie fibery beda mialy proc o nazwie qqq
#      fiber$id eval {... skrypt ...}
#        wykonuje skrypt w fiberze $id na poziomie bieżącym 
#        (tam gdzie fiber jest zamrożony)
#
# 2. mozna zmienic glowny program fiberow bez ich usuwania przy pomocy:
#      fiber code { ... nowy program glowny ...}
#        # jednak nowy program glowny zacznie obowiazywac dopiero
#        # gdy wszystkie fibery zakancza stary program glowny!
#
# 3. model asynch./ proc pomocnicza czytajKomTypu
#      proc czytajKomTypu {typ nrPol}
#        czeka na komunikat typu $typ z polaczenia $nrPol
#        (chyba ze podamy * co oznacza dowolny typ/nr polaczenia)
#        typ komunikatu to pierwsze słowo listy, która jest komunikatem
#        (np dla komunikatu {Q 123} typem jest "Q")
#        zwracany jest cały komunikat, razem z typem
#        komunikat jest usuwany ze zmiennej kom$nrPol (lub innej)
#        można podać jako 3 param. nazwę zm. w której otrzymamy nr poł. 
#        (o ile $nrPol=="*"; np czytajKomTypu Q * nrpol)
#        UWAGA: ta proc nie we wszystkich algorytmach asynch jest przydatna...
#

# Problemy z j. Tcl (patrz też tutorial j. Tcl):
#
# 1. jak zadeklarować zmienne globalne w procedurze ?
#    proc p1 {} {
#      global zm_glob; puts $zm_glob
#    }
#    proc p1 {} {
#      puts $::zm_glob; # nie deklarujemy, tylko "nazwa kwalifikowana" zmiennej
#    }
#
# 2. kłopoty z konsola2c.tcl (10.2018)
#    jeśli nie widać selekcji to wykonaj kod w konsoli:
#      .konsola.t config -selectborderwidth 1
#    jeśli nie działają klawisze Ctrl/Shift+Ins (copy/paste) to wtedy
#    zobaczyć jakie klawisze powodują copy/paste; z konsoli wykonać:
#      event info <<Copy>>
#      event info <<Paste>>
#      
 

# ---
 
# Przyklad symulacji modelu synchronicznego
# + w tym przykładzie po sieci krąży token zawierający zmienną typu "int",
#   której wartość zwiększa się o 1 po każdym skoku ...
#
 
source symul_lib.tcl; # ladowanie symulatora
 
# tworzymy graf komunikacyjny (w tym wypadku cykl)
set liczbaWierz 5
set sasiedzi(0) {4 1}
set sasiedzi(1) {0 2}
set sasiedzi(2) {1 3}
set sasiedzi(3) {2 4}
set sasiedzi(4) {3 0}
 
# główny program na każdym wierzchołku/fiberze...
fiber create $liczbaWierz {
 
  if {$id==0} {wyslij 1 0}
  fiber yield; # oznacza koniec rundy
 
  while {$run} { # zmienna run pozwala zakonczyć działanie symulacji
    if {$kom(0)!=""} {
      set x $kom(0)
      incr x
      wyslij 1 $x
    }
    fiber yield; # oznacza koniec rundy
  }
}
 
Inicjalizacja; # koniecznie trzeba to wywołać !!!
 
proc wizualizacja {} {
  fiber_iterate {_puts "$id: $kom0, $kom1"}
    # petla fiber_iterate iteruje po wszystkich fiberach
    # proc wizualizacja wywolujemy z konsoli po kazdej rundzie
}
 
# !!! do tego miejsca wszystko wykonać !!!!!!!!!!!!!!!!!!!!!

fiber yield; runda; wizualizacja
  # wykonuje kolejna runde...
  # procedura runda dostarcza wysłane komunikaty
 
if 0 { # to się czasem przydaje:
set_run 0; fiber yield; runda; set_run 1; fiber delete
  # usuwanie fiberów
set_run 0; fiber yield; runda; set_run 1; fiber restart
  # restart kodu fiberów
fiber error
  # wyświetla stan fiberów ({}, ended, error)
fiber_eval 0 {set id}
  # wykonanie kodu w fiberze 0
  # UWAGA: fiber_eval wykonuje kod na poziomie globalnym
  #   "fiber0 eval {set id}" wykonuje kod tam gdzie fiber zostal zamrozony...
}

# --- 
 
# Przyklad symulacji modelu asynchronicznego
# + w tym przykladzie wierz. wysylaja i przekazuja
#   komunikaty postacai "Q $id_los"
#
 
source symul_lib.tcl
 
set liczbaWierz 20
iterate i $liczbaWierz {
    # petla "iterate i 10" iteruje od 0 do 9
  let i1 $i-1; if {$i1==-1} {let i1 $liczbaWierz-1}
  let i2 $i+1; if {$i2==$liczbaWierz} {let i2 0}
  set sasiedzi($i) "$i1 $i2"
}
 
fiber create $liczbaWierz {
    wyslij 1 "Q $id_los"
    fiber switchto main
    while {$run} {
      if {$kom(0)!=""} {
        set x [czytaj 0]
        wyslij 1 $x
      }
      fiber switchto main
    }
}
InicjalizacjaAsynch
 
proc wizualizacja {} {
  fiber_iterate {_puts "$id, $id_los, $lider; $kom0, $kom1"}
}
 
# ... do tego miejsca mozna wszystko wykonac
 
if 0 {
zakonczFibery; fiber delete
zakonczFibery; fiber restart
fiber error
zaproponuj
pokazKom
set licznikKom
}
 
fiber switchto 8; pokazKom
dostarczKom 9 0; pokazKom
fiber switchto 9; pokazKom
  # + tworzymy egzekucje ...
  #   (tj. ciag zdrzen: "obliczeniowych" i "dostarczenia komunikatu")
  # + "fiber switchto nr_wierz" - to zdarzenie obliczeniowe
  # + "dostarczKom nr_wierz nr_kabla" - to zdarzenie dostarczenia komunikatu
  # + "pokazKom" to proc. wizualizacyjna, pokazująca stan wszystkich połączeń

# ---

# Przyklad 2 symulacji modelu asynchronicznego
# + jest to alg. wyboru lidera, uzywajacy O(n^2) komunikatow
# + UWAGA! w symulatorze asynch. wierzchołki powinny pracować "w nieskończoność"
#   symulację wył. "ręcznie" gdy stwierdzimy, że nie wysyła się już żadnych komunikatów ...
#

source symul_lib.tcl
 
set liczbaWierz 20
iterate i $liczbaWierz {
    # petla "iterate i 10" iteruje od 0 do 9
  let i1 $i-1; if {$i1==-1} {let i1 $liczbaWierz-1}
  let i2 $i+1; if {$i2==$liczbaWierz} {let i2 0}
  set sasiedzi($i) "$i1 $i2"
}
 
fiber create $liczbaWierz {
    set lider {}
    wyslij 1 "LE $id_los"
    while {$run} {
      set id0 [lindex [czytajKomTypu LE 0] 1]
      if {$id0>$id_los} {
        wyslij 1 "LE $id0"
      } elseif {$id0==$id_los} {
        set lider 1; wyslij 1 "LE -1"
      } elseif {$id0==-1 && $lider=={}} {
        set lider 0; wyslij 1 "LE -1"
      }
    }
}
InicjalizacjaAsynch
 
proc wizualizacja {} {
  fiber_iterate {_puts "$id, $id_los, $lider; $kom0, $kom1"}
}
 
# ... do tego miejsca mozna wszystko wykonac
 
if 0 {
zakonczFibery; fiber delete
zakonczFibery; fiber restart
fiber error
zaproponuj
pokazKom
set licznikKom
}
 
fiber switchto 8; pokazKom
dostarczKom 9 0; pokazKom
  # + tworzymy egzekucję ...


# ---


## dokumentacja wyciągnięta z pliku symul_lib.tcl (!!! niedokończone !!!)
#

# koniec rundy zaznaczamy przez "fiber yield"

# wierz stopnia np 3 ma incydentne kraw oznaczone przez:
#   kom0, kom1, kom2

# kazda incydentna kraw nr X ma 2 zmienne:
#   komX - zawiera komunikat wyslany przez sasiada w poprzedniej rundzie
#   komX_pisz - tu jest umieszczany komunikat wyslany przez sasiada w biezacej rundzie
#   Uwaga:
#     komX to lista komunikatow, a nie pojedynczy komunikat!!!
#     (jesli komunikat nie ma spacji to lista sie nie rozni
#     od pojedynczego komunikatu!!! to moze byc przyczyna bledow...)

# proc komX_wyslij komunikat
#   sluzy do wysylania komunikatu przez kraw nr X
#   np
#      kom${i}_wyslij "A ku ku"
#      kom1_wyslij $x

# proc komX_dostarcz komunikat (16.11.2009)
#   wysyla i dostarcza komunikat...

# proc wyslij {doKogo co args}
#   wysyla komunikat przez polaczenie nr doKogo
#     np: wyslij $i "A ku ku"
#   podobnie dziala dostarcz (16.11.2009)

# proc czytaj {odKogo args}
#   odbiera komunikat z polaczenia nr odKogo
#   w args moze byc opcja -podgraf {lista nr polaczen}
#   komendy wyslij/czytaj to interf. "wyzszego poziomu"
#     umozliwia pewne uogolnienia, np dzialania na podgrafie:
#       wyslij 0 "123" -podgraf {2 3}; # wysyla przez pol. nr 2
#       czytaj 1 -podgraf {3 4}; # odczytuje z pol. nr 4
#         !!! jeszcze nie zaimplementowane !!!
#   16.11.2009 - zmiana dzialania:
#     czytaj pobiera komunikat z komX, jako kolejki komunikatow

# proc. pomocnicze w konsoli:
#   DodajKraw
#   Inicjalizacja
#   runda
#   set_run
#   fiber_iterate
#   fiber_eval

# proc. pomocnicze w fiberze:
#   iterate zm liIter kod
#   iterate1 zm liIter kod; # robi liIter-1 iteracji od 1 !!!
#   let
#   comment


## model asynch z osobnymi zdarzeniami obl. i dostarczania (19.10.2009)
# * wysylanie komunikatu przy pomocy:
#     komX_wyslij msg
# * dostarczone komunikaty sa dostepne w zmiennych komX;
#   oczekiwanie na dostarczenie komunikatu okreslnego typu:
#     czytajKomTypu {typ nrPolaczenia}
#     zaklada sie ze komunikat to lista >=2 elementowa, pierwsze slowo to typ
#     jesli brak potrzebnego komunikatu to proc wykonuje "fiber switchto main"
# * generowanie zdarzenia obliczeniowego:
#     fiber switchto procesor
# * generowanie zdarzenia dostarczenia komunikatu:
#     dostarczKom procesor nrPolaczenia
#     przemieszcza komunikaty z komX_pisz do komX
# * podglad dostepnych komunikatow:
#     pokazKom
# * proc pomocnicze:
#     zakonczFibery
#     zaproponuj
#       proponuje zdarzenia ktore cos zmienia...

# proc czytajKomTypu {typ nrPolaczenia {varNrPol ""}}
#   zaklada sie ze w kazdym komunikacie pierwsze slowo to typ...
#   mozna podac "*" jako nrPolaczenia i/lub typ !!!
#   rzeczywisty nr jest zwracany przez (opcjonalny) varNrPol


uwaga: portal używa ciasteczek tylko do obsługi tzw. sesji...