Simpy - Harjoitustehtävä 3
Harjoitustehtävä 3 - Turvatarkastus
[muokkaa | muokkaa wikitekstiä]Simulointia 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))