Simpy - Harjoitustehtävä 3
Harjoitustehtävä 3 - Turvatarkastus
muokkaaSimulointia käytetään yleisesti tilasteissa, jossa tutkitaan jonon muodostumista. Lentokentän turvatarkastuksen simulointi on klassinen esimerkki jonotuksesta.
Tee ohjelma, joka simuloi lentokentän turvatarkastuksen jonoa kello 08-13 välisenä aikana. Lentokentällä matkustajat saapuvat turvatarkastukseen yhdestä paikasta. Matkustajia saapuu tarkastukseen eri määrä eri kellonaikoina. Saapumisten vaihteluväli sekunteina on seuraavassa taulukossa.
Kellon aika | Min | Max |
---|---|---|
Klo 8-10 | 15 s | 40 s |
Klo 10-12 | 10 s | 20 s |
Klo 12-13 | 10 s | 30 s |
Kentällä on 4 turvatarkastuslinjaa. Yksi linjoista on aina auki. Kun jonossa on yli 10 henkilöä, avataan toinen linja, kolmas linja avataan kun jonossa on 15 henkilöä ja neljäs linja kun jonossa on 20 henkilöä. Vastaavasti linja suljetaan, kun jonon pituus laskee alle sen käynnistämisrajan. Jonossa olevien matkustajien määrää tarkastellaan 10 minuutein välein.
Matkustajan turvatarkastuaika vaihtelee 40-90 sekunnin välillä.
Simuloinnin päätteeksi tulee tulostaa kunkin linjan kautta kulkeneiden matkustajien määrä, samoin kokonaismäärä. Lisäksi tulee esittää turvatarkastuksen jonon pituus ja matkustajien keskimääräinen turvatarkastukseen odotusaika kymmenen minuutin aikavälein tarkasteluajalta.
Esimerkkiratkaisu
Huom! Apufunktioiden kello(env) ja kello2(aika) kommentointi lopullisessa ohjelmakoodissa.
Turvatarkastusjono on helpointa simuloida Store-tyyppisenä varastona, johon yksi prosessi generoi matkustajia ja josta turvatarkastusprosessit ottavat sitten matkustajan tarkastukseen.
Simpyssä Store-tyyppisen varaston luomme
jono=simpy.Store(env)
Tarkastukseen saapuva matkustaja on selvästi olio, jonka saapumisaika (luontiaika) on otettava talteen, koska joudumme laskemaan keskimääräistä odotusaikaa.
class Matkustaja: def __init__(self, tunniste, saapumisaika): self.__tunniste = tunniste self.__saapumisaika = saapumisaika self.__odotusaika = 0 def get_tunniste(self): return self.__tunniste def get_saapumisaika(self): return self.__saapumisaika def set_odotusaika(self, odotusaika): self.__odotusaika = odotusaika def get_odotusaika(self): return self.__odotusaika
Nyt Simpyn aikayksikköä tulee käsitellä sekuntina. Muutoin matkustajien generointiprosessi on aika selväpiirteinen.
def Generoi_matkustajia(env, jono): i=0 while True: aika = env.now if aika< 36000: #Klo 08:0010:00 saapumisvali = random.randint(min08, max08) if aika >= 36000 and aika < 43200: #Klo 10:00-12:00 saapumisvali = random.randint(min10, max10) if aika >= 43200: #Klo 12:00-> saapumsivali = random.randint(min12, max12) yield env.timeout(saapumisvali) matkustaja = Matkustaja(i, aika) jono.put(matkustaja) i=i+1
Myös itse turvatarkastus on periaatteessa yksinkertainen: otetaan matkustaja jono-varastosta, odotetaan turvatarkastukseen menevä aika ja aloitetaan alusta. Näin toimii ainakin yksi prosessi, joka on toiminnassa koko ajan. Haasteeksi tulee kuinka käynnistämme seuraavat tarkastuslinjat, kun niitä tarvitaan ja pysäytämme ne, kun tarkastusjono on lyhentynyt tarpeeksi.
Tässä avuksi tulee Simpyn Interrupt-menettely, jonka opimme kappaleessa 3. Tehtäväannossa määrättiin, että turvajonon pituutta tarkastellaan 10 minuutin välein. Tarvitsemme siis prosessin, joka kymmennen minuutin välein käy tarkastamassa jonon pituuden ja sen mukaan joko käynnistää uuden tarkastusprosessin tai sulkee sen. Se onko tarkastuslinja käytössä vai ei, voidaan helposti hoitaa "lipputietomuuttujalla". Linjojen käynnistämisen/pysäyttämisen raja-arvot (=jonon pituus) voidaan laittaa muuttujien arvoksi, jotta niiden muuttaminen olisi helppo, jos haluamme simuloida malli toisilla arvoilla. Lisäksi meidän pitää ottaa talteen turvajonopituus List-muuttujaan, jotta voimme myöhemmin raportoida jonon pituuksia eri aikoina.
Nimettäköön koko ajan käynnissä olevasta tarkastuslinjaprosessi Linja1'ksi ja muut sitten Linja2, Linja3 ja Linja4. Simpy Interrupt - menettely edellyttää, että Linja-prosessin käynnistyessä prosessi osoitetaan muuttujalle, johon voidaan viitata, kun prosessi halutaan pysäyttää.
Otamme tässä esimerkiksi Interrupt - menettelyyn myös uuden cause - parametrin käyttöön. Cause-paramterin avulla voidaan keskeytettävään prossiin viedä tieto keskeyttämisen syystä.
maxjono1=10 maxjono2=15 maxjono3=20 jonopituus = []
def Linjakontrolli(env, jono): linja2_kaynnissa = False linja3_kaynnissa=False linja4_kaynnissa=False while True: jonopituus.append(len(jono.items)) #Otetaan talteen jono pituu ko. ajanhetkenä '#Käynnistetään lisää linjoja, kun 1) jono ylittää raja-arvon ja 2) ko. linjaprosessi ei ole käynnissä if len(jono.items) > maxjono1 and linja2_kaynnissa == False: print(kello(env),'Aktivoidaan Linja2. Jonossa:',len(jono.items)) l2=env.process(Linja2(env, jono)) linja2_kaynnissa = True if len(jono.items) > maxjono2 and linja3_kaynnissa == False: print(kello(env),'Aktivoidaan Linja3. Jonossa:',len(jono.items)) l3=env.process(Linja3(env, jono)) linja3_kaynnissa = True if len(jono.items) > maxjono3 and linja4_kaynnissa == False: print(kello(env),'Aktivoidaan Linja4. Jonossa:',len(jono.items)) l4=env.process(Linja4(env, jono)) linja4_kaynnissa = True '#Keskeytetään linjaprosessi, kun jonon pituus alittaa raja-arvon ja 2) prosessi on käynnissä if len(jono.items) < maxjono3 and linja4_kaynnissa == True: cause='matkustajien vähäisyyden vuoksi' simpy.events.Interruption(l4,cause) linja4_kaynnissa=False if len(jono.items) < maxjono2 and linja3_kaynnissa == True: cause='matkustajien vähäisyyden vuoksi' simpy.events.Interruption(l3,cause) linja3_kaynnissa=False if len(jono.items) < maxjono1 and linja2_kaynnissa == True: cause='matkustajien vähäisyyden vuoksi' simpy.events.Interruption(l2,cause) linja2_kaynnissa=False yield env.timeout(600) #600 aikayksikkö = 10 minuuttia
Jatkuvasti toiminnassa oleva tarkastuslinjaprosessi, eli Linja1 on varsin selkeä, kunhan vain muistamme, että meidän pitää laskea ja ottaa talteen käsitellyn matkustajan odotusaika sekä linjan läpi kulkeneiden matkustajien määrä. Lisäksi matkustaja-olio pitää ottaa talteen lista-muuttujaan, jotta ohjelman lopussa voidaan laskea keskimääräiset odotusajat.
matkustajatiedot = [] linjat = [0,0,0,0]
def Linja1(env, jono): while True: matkustaja = yield jono.get() #Otetaan seuraava matkustaja jonosta tarkastusaika = random.randint(minaika,maxaika) #'Arvotaan' tarkastuaaika yield env.timeout(tarkastusaika) #Tarkastetaan matkustaja #print(kello(env), 'Matkustaja',matkustaja.get_tunniste(),'pääsi turvatarkastuksesta läpi. Linja 1') odotusaika = env.now - matkustaja.get_saapumisaika() # Lasketaan odotusaika matkustaja.set_odotusaika(odotusaika) #Laitetaan odotussika talteen olioon matkustajatiedot.append(matkustaja) #Lisätään matkustaja-olio matkustajatiedot - list-muuttujaan linjat[0]=linjat[0]+1 #Kasvatetaan linjalla läpikulkeneiden matkustajien määrään (huom! Linja1 = linjat[0])
Linjojen 2-4 prosessi on muutoin sama, mutta jotta pystymme sen tarvittaessa lopettamaan, meidän tulee ottaa käyttöön Pythonin try-except - käytäntö, kuten kappalessaa 4 opimme.
def Linja2(env,jono3): while True: try: #Linja käynnissä matkustaja = yield jono.get() tarkastusaika = random.randint(minaika,maxaika) yield env.timeout(tarkastusaika) #print(kello(env), 'Matkustaja',matkustaja.get_tunniste(),'pääsi turvatarkastuksesta läpi. Linja 2') odotusaika = env.now - matkustaja.get_saapumisaika() matkustaja.set_odotusaika(odotusaika) matkustajatiedot.append(matkustaja) linjat[1]=linjat[1]+1 except simpy.Interrupt as i: #Linjan toiminta keskeytetään print(kello(env),'Linja 2 suljettu', i.cause,'Jonossa:',len(jono.items)) break
Linjojen 3 ja 4 koodi on vastaavanlaiset kuin edellä. Lopuksi tarvitsemme sitten vielä pääohjelman ja tilastointiosuuden.
env=simpy.Environment(28800) jono=simpy.Store(env) env.process(Generoi_matkustajia(env,jono)) env.process(Linjakontrolli(env,jono)) l1=env.process(Linja1(env,jono)) env.run(until=50400) '#Tilastointiosuus print('Turvatarkaslinjojen läpilukeneet markustajat') print('Linja1:',linjat[0],'Linja2:',linjat[1],'Linja3:',linjat[2],'Linja4:',linjat[3]) print('Jonon pituus: Kello : Pituus (hlö):') for i in range(0,int(len(jonopituus))): aika = 28800+600*i print(kello2(aika), jonopituus[i]) print('Keskimääräinen jonotusaika:') for i in range (0, len(matkustajatiedot),10): matkustaja = matkustajatiedot[i] aika = matkustaja.get_saapumisaika() print(kello2(aika),':', int(matkustaja.get_odotusaika()/60))
Esimerkkiratkaisun ohjelmakoodi kokonaisuudessaan apufunktioineen:
import simpy import random matkustajatiedot = [] jonopituus = [] linjat = [0,0,0,0] '#Matkustajien saapumisvälit (min,max, sekuntia) '#Kello 08-10, Kello 10-12 ja kello 12-> min08=15 max08=40 min10=10 max10=20 min12=10 max12=30 maxjono1=10 maxjono2=15 maxjono3=20
'#Turvatarkastuksen läpimenoajan rajat, sekuntia
minaika=40 maxaika=90 class Matkustaja: def __init__(self, tunniste, saapumisaika): self.__tunniste = tunniste self.__saapumisaika = saapumisaika self.__odotusaika = 0 def get_tunniste(self): return self.__tunniste def get_saapumisaika(self): return self.__saapumisaika def set_odotusaika(self, odotusaika): self.__odotusaika = odotusaika def get_odotusaika(self): return self.__odotusaika def kello(env): '#Funtio palauttaa env.now ajan muodossa hh:mm - huom. sekunnit on '#jätetty pois aika=env.now minuutit= int(aika/60) tunnit = int(minuutit/60) minuutit = minuutit-tunnit*60 if tunnit < 10: stunnit='0'+str(tunnit) else: stunnit = str(tunnit) if minuutit < 10: sminuutit = '0'+str(minuutit) else: sminuutit = str(minuutit) staika = stunnit+":"+sminuutit return staika def kello2(aika): '#Funktio muuttaa sekunteina annetun ajan muotoonh hh:mm minuutit= int(aika/60) tunnit = int(minuutit/60) minuutit = minuutit-tunnit*60 if tunnit < 10: stunnit='0'+str(tunnit) else: stunnit = str(tunnit) if minuutit < 10: sminuutit = '0'+str(minuutit) else: sminuutit = str(minuutit) staika = stunnit+":"+sminuutit return staika def Linjakontrolli(env, jono): linja2_kaynnissa = False linja3_kaynnissa=False linja4_kaynnissa=False while True: jonopituus.append(len(jono.items)) '#Käynnistetään lisää linjona, kun 1) jono ylittää raja-arvon ja 2) ko. linjaprosessi ei ole käynnissä if len(jono.items) > maxjono1 and linja2_kaynnissa == False: print(kello(env),'Aktivoidaan Linja2. Jonossa:',len(jono.items)) l2=env.process(Linja2(env, jono)) linja2_kaynnissa = True if len(jono.items) > maxjono2 and linja3_kaynnissa == False: print(kello(env),'Aktivoidaan Linja3. Jonossa:',len(jono.items)) l3=env.process(Linja3(env, jono)) linja3_kaynnissa = True if len(jono.items) > maxjono3 and linja4_kaynnissa == False: print(kello(env),'Aktivoidaan Linja4. Jonossa:',len(jono.items)) l4=env.process(Linja4(env, jono)) linja4_kaynnissa = True '#Keskeytetään linjaprosessi, kun jonon pituus alittaa raja-arvon ja 2) prosessi on käynnissä if len(jono.items) < maxjono3 and linja4_kaynnissa == True: cause='matkustajien vähäisyyden vuoksi' simpy.events.Interruption(l4,cause) linja4_kaynnissa=False if len(jono.items) < 15 and linja3_kaynnissa == True: cause='matkustajien vähäisyyden vuoksi' simpy.events.Interruption(l3,cause) linja3_kaynnissa=False if len(jono.items) < 10 and linja2_kaynnissa == True: cause='matkustajien vähäisyyden vuoksi' simpy.events.Interruption(l2,cause) linja2_kaynnissa=False yield env.timeout(600) def Generoi_matkustajia(env, jono): i=0 while True: aika = env.now if aika< 36000: saapumisvali = random.randint(min08, max08) if aika >= 36000 and aika < 43200: saapumisvali = random.randint(min10, max10) if aika >= 43200: saapumsivali = random.randint(min12, max12) yield env.timeout(saapumisvali) matkustaja = Matkustaja(i, aika) jono.put(matkustaja) i=i+1 def Linja1(env, jono): while True: matkustaja = yield jono.get() #Otetaan seuraava matkustaja jonosta tarkastusaika = random.randint(minaika,maxaika) #'Arvotaan' tarkastuaaika yield env.timeout(tarkastusaika) #Tarkastetaan matkustaja #print(kello(env), 'Matkustaja',matkustaja.get_tunniste(),'pääsi turvatarkastuksesta läpi. Linja 1') odotusaika = env.now - matkustaja.get_saapumisaika() # Lasketaan odotusaika matkustaja.set_odotusaika(odotusaika) #Laiktetaan odotussika talteen olioon matkustajatiedot.append(matkustaja) #Lisätään matkustaja-olio matkustajatiedot - list-muuttujaan linjat[0]=linjat[0]+1 #Kasvatetaan linjalla läpikulkeneiden matkustajien määrään (huom! Linja1 = linjat[0]) def Linja2(env,jono3): while True: try: matkustaja = yield jono.get() tarkastusaika = random.randint(minaika,maxaika) yield env.timeout(tarkastusaika) #print(kello(env), 'Matkustaja',matkustaja.get_tunniste(),'pääsi turvatarkastuksesta läpi. Linja 2') odotusaika = env.now - matkustaja.get_saapumisaika() matkustaja.set_odotusaika(odotusaika) matkustajatiedot.append(matkustaja) linjat[1]=linjat[1]+1 except simpy.Interrupt as i: print(kello(env),'Linja 2 suljettu', i.cause,'Jonossa:',len(jono.items)) break def Linja3(env,jono3): while True: try: matkustaja = yield jono.get() tarkastusaika = random.randint(minaika,maxaika) yield env.timeout(tarkastusaika) #print(kello(env), 'Matkustaja',matkustaja.get_tunniste(),'pääsi turvatarkastuksesta läpi. Linja 3') odotusaika = env.now - matkustaja.get_saapumisaika() matkustaja.set_odotusaika(odotusaika) matkustajatiedot.append(matkustaja) linjat[2]=linjat[2]+1 except simpy.Interrupt as i: print(kello(env),'Linja 3 suljettu', i.cause,'Jonossa:',len(jono.items)) break def Linja4(env,jono3): while True: try: matkustaja = yield jono.get() tarkastusaika = random.randint(minaika,maxaika) yield env.timeout(tarkastusaika) #print(kello(env), 'Matkustaja',matkustaja.get_tunniste(),'pääsi turvatarkastuksesta läpi. Linja 4') odotusaika = env.now - matkustaja.get_saapumisaika() matkustaja.set_odotusaika(odotusaika) matkustajatiedot.append(matkustaja) linjat[3]=linjat[3]+1 except simpy.Interrupt as i: print(kello(env),'Linja 4 suljettu', i.cause,'Jonossa:',len(jono.items)) break env=simpy.Environment(28800) jono=simpy.Store(env) env.process(Generoi_matkustajia(env,jono)) env.process(Linjakontrolli(env,jono)) l1=env.process(Linja1(env,jono)) env.run(until=50400) print('Turvatarkaslinjojen läpilukeneet markustajat') print('Linja1:',linjat[0],'Linja2:',linjat[1],'Linja3:',linjat[2],'Linja4:',linjat[3]) print('Jonon pituus: Kello : Pituus (hlö):') for i in range(0,int(len(jonopituus))): aika = 28800+600*i print(kello2(aika), jonopituus[i]) print('Keskimääräinen jonotusaika:') for i in range (0, len(matkustajatiedot),10): matkustaja = matkustajatiedot[i] aika = matkustaja.get_saapumisaika() print(kello2(aika),':', int(matkustaja.get_odotusaika()/60))