Simpy - Harjoitustehtävä 2

Wikikirjastosta

Simpy - Harjoitustehtävä 2: Satama[muokkaa | muokkaa wikitekstiä]

Tämä on hyvin klassinen simulointitehtävä. Kuvitellaan satama, jossa on kolmenlaisia laiturpaikkoja ja johon saapuu siten myös kolmentyyppisä aluksia, joilla on eri kantavuus. A-tyypin laituri voi ottaa vastaan kaikkia laivatyyppejä A,B ja C. B-tyypin laituriin voi tulla vain B-tyypin laivoja. C-laituriin voi tulla C-tyypin laivoja sekä B-tyypin laivoja, joiden kantavuus on korkeintaan 5000 tonnia.

Satamassa on yksi A-tyypin laituri, kaksi B-tyypin laituria ja 3 C-tyypin laituria. Laivoista 3/11 osaa on A-tyypin laivoja ja B- ja C-tyypin laivoja kumpiakin 4/11 osaa. A-laivatyypin kantavuus vaihtelee 2000-12000 tonnin välillä, B-laivatyypin 2000-80000 välillä ja C-laivatyypin 2000-5000 tonnin välillä. Purettavaa ja lastattavaa tavaraa on kussakin laivassa satunnaisesti kantavuuden rajoissa.

Jokaisella laiturityypillä on oma lastaus- ja purkunopeus:

Laiturityyppi Purkunopeus tn/min Lastausnoperus tn/min
A 200 200
B 100 75
C 75 60

Satama avautuu kello 08:00 ja viimeinen laiva otetaan sisään viimeistään kello 20:00, jonka jälkeen satamasta vain lähtee laivoja. Jokainen saapuva laiva purkaa ja lastaa tavaraa oman kantavuuden rajoissa satunnaisia määriä. Laivoja saapuu satunnaisesti 10-30 minuutin väliajoin.

Satamatoimintoja ohjataan niin, että ensisijaisesti alus ohjataan oman tyyppinsä mukaiseen laituriin. Mikäli vapaata oman tyypin laituria ei ole saatavissa 5 minuutin kuluessa, voidaan kysyä vapaata paikkaa A-laiturista (B-ja C- tyypin alukset). Jos sitäkään ei löydy, jäädään kiltisti odottamaan oman tyypin laituripaikan vapautumista. Poikkeuksen tekee B-tyypin laiva, jonka kantavuus on alle 5000 tonnia: se voi A-tyypin laiturin lisäksi päästä myös vapaaseen C-laituriin.

Tee ohjelma, joka simuloi sataman toimintaa yhden työpäivän ajan, tulostaa satamatapahtumia, laskee eri laivatyyppien määrän, koko sataman puretun ja lastatun tavaran kokonaismäärän, sekä laivojen keskikmääräisen satamassaoloajan sekä keskimääräisen laituripaikan odotusajan.

Esimerkkiratkaisu

Tässä tehtävässä ei ole paljonkaan uutta oppia Simpystä, mutta se on hyvä esimerkki millaisia sovelluksia simuloinnilla voidaan toteuttaa.

Aloitamme laivasta. Jokainen saapuva laiva on ilmiselvä olio, joka luodaan sen saapuessa satamaan. Sillä on tunnetut piirteet: tunnistetieto, typppi, kantavuus (B-tyypin aluksilla merkittävä), purettavan ja lastattavan lastin määrä, saapumisaika, satamassa oloaika ja odostuaika. Näille tiedoille tarvitaan metodit niiden lukemiseksi ja osalle myös niiden asettamiseksi olion tultua luoduksi. Luokkamäärittely voisi olla siten seuraava:

class Laiva:
   def __init__(self, tunnus, kantavuus, tyyppi, purettavaa, lastattavaa, aika):
       self.__tunnus = tunnus
       self.__kantavuus = kantavuus
       self.__tyyppi = tyyppi
       self.__purettavaa = purettavaa
       self.__lastattavaa = lastattavaa
       self.__aika=aika #Luontiaika = saapumisaika satamaan
       self.__odotusaika = 0
       self.__satama_aika = 0 #Satamassa käytetty koknaisaika
   def get_tunnus(self):
       return self.__tunnus
   def get_kantavuus(self):
       return self.__kantavuus
   def get_tyyppi(self):
       return self.__tyyppi
   def get_purettavaa(self):
       return self.__purettavaa
   def get_lastattavaa(self):
       return self.__lastattavaa
   def get_aika(self):
       return self.__aika
   def get_satama_aika(self):
       return self.__satama_aika
   def get_odotus(self):
       return self.__odotusaika
   def set_odotus(self, odotusaika):
       self.__odotusaika = odotusaika
   def set_satama_aika(self,satama_aika):
       self.__satama_aika = satama_aika

Vastaavasti voidaan todeta, että laituri on luokka, jossa on resursseja (Resource) laivojen varattavaksi (kts Tehtävä 2). Tarvitsemme kolmetyyppisiä laitureita erinäisen määrän. Laiturin luokkamääritys voisi siten olla:

class Laituri:
   def __init__(self,env, tyyppi, paikkoja):
       self.env = env
       self.tyyppi = tyyppi
       self.paikkoja = simpy.Resource(env, paikkoja)

Aikanaan pääohjelmassa luomme sitten nuo tehtäväasetannan mukaiset laiturit seuraavasti:

laituriA = Laituri(env, 'A', 1)
laituriB = Laituri(env, 'B', 2)
laituriC = Laituri(env, 'C', 3)

Laivojen generointi noudattaa jo oppimaamme käytäntöä. Tarvitsemme siis prosessin, joka generoi laivoja kaikille satamaan tuleviksi.

Laivatyyppejä A,B, ja C pitää generoida suhteessa 3/11,4/11 ja 4/11. Se käy kätevästi Pythonin random.choice([lista]] – funktiolla, joka valitsee satunnaisesti jonkun listan alkiosta. Tyyppien suhde menee oikein, kun listassa on oikea määrä kutakin typpiä:

tyyppi = random.choice(['A','A','A','B','B','B','B','B','C','C','C','C'])

Laivojen kantavuudella ei ole merkityistä muille kuin B-tyypin aluksille, koska alle 5000 tonnin B-tyypin alus voi käyttää myös C-tyypin laituria. Laivojen purettavan ja lastattavan lastin määrän voi generoida tasatonnein helposti random.randint – funktiolla. B-tyypin laivoilla lastin määrää ’arvottaessa’ pitää muistaa, ettei laivan kantavuutta kuitenkaan ylitetä.

Kun kaikki laivan perustiedot on ’arvoittu’ voidaan laiva-olio luoda:

laiva = Laiva(tunnus, kantavuus, tyyppi, purettavaa, lastattavaa, aika)

Sitten ei jääkään muuta tehtävää kuin generoida prosessi, jossa luotu laiva alkaa pyytämään laituripaikkaa

env.process(laituripyynto(env, laiva, laituriA, laituriB, laituriC))

ja sen jälkeen jäädä odottamaan sopiva ajanjakso ennen uuden laivan luomista. Vielä meidän tulee ottaa huomioon, että laivoja otetaan vastaan vain klo 20:00 saakka (= simpyaikana 1200)

def GenLaivoja(env,laituriA, laituriB, laituriC):
   tunnus = 0
   while True:
     if env.time <= 1200:
           kantavuus = 0
           aika=env.now
           tyyppi = random.choice(['A','A','A','B','B','B','B','B','C','C','C','C'])
           if tyyppi == 'A':
               purettavaa = 1000*random.randint(8,12)
               lastattavaa = 1000*random.randint(8,12)
           if tyyppi == 'B':
               kantavuus=random.randint(2,8) #B-tyypin aluksille pitää määrittää kantavuus
               purettavaa = 1000*random.randint(2,kantavuus)
               lastattavaa = 1000*random.randint(2,kantavuus)
               kantavuus=1000*kantavuus
           if tyyppi == 'C':
               purettavaa = 1000*random.randint(2,5)
               lastattavaa = 1000*random.randint(2,5)
           laiva = Laiva(tunnus, kantavuus, tyyppi, purettavaa, lastattavaa, aika)
           print(kello(env),'Laiva', tunnus, ',tyyppi;',tyyppi, ',kantavuus:', kantavuus,  
                    ',purettavaa:', purettavaa,' lastettavaa:', lastattavaa, 'saapuu')
           env.process(laituripyynto(env, laiva, laituriA, laituriB, laituriC))
           yield env.timeout(random.randint(10,30))
           tunnus=tunnus+1
    else:   '# Kello 20:00 laivojen vastaanotto lopetaan
       break

Laituripaikan pyyntöprosessissa joudumme käyttämään tehtävässä 4 käytettyä rinnakkaisten prosessien ’kilpailua’ sekä tarkastamaan resurssin jonotustilannetta. Jokaiselle laivatyypille joudumme tekemään oman päättelyprosessin tehtävänannon mukaisesti.

A-tyypin laivoille laituripaikan pyyntön on varsin yksinkertainen, koska se voi rantautua vai A-tyypin laituriin. Niinpä alku on helppo: laituripaikkaa pyydetään laituriA – oliolta jo tutulla tavalla:

with laituriA.paikkoja.request() as  pyynto:
  yield pyynto

Kun laituripaikka joskus saadaan, voidaan aloittaa laituritoiminnotA – prosessi, joka purkaa ja lastaa laivan A-laiturille määritetyin nopeuksin (tonnia/min). Huom! Laituritoiminnot-prosessia pitää kutsua yield’illä, jotta se varmasti toteutuu, ennenkuin laiva pääsee lähtemään satamasta.

yield env.process(laituritoiminnotA(env, laiva))
def laituripyynto(env, laiva, laituriA, laituriB, laituriC):
   Apaikkoja=1
   Bpaikkoja=2
   Cpaikkoja=3
   tyyppi = laiva.get_tyyppi()
   if tyyppi == 'A':
       with laituriA.paikkoja.request() as  pyynto:
            yield pyynto
            print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
            yield env.process(laituritoiminnotA(env, laiva))
            print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')

Tyypin C-laivoilla on seuraavaksi helpoin laituripyyntö: Mikäli jossakin kolmesta C-tyypin laituripaikassa on tilaa, mennään sinne jos 5 minuutin sisään päästään.

with laituriC.paikkoja.request() as pyynto
  tulos = yield pyynto | env.timeout(5)

Jos kaikki C-laiturit ovat varattuja, katsotaan, onko A-laiturissa tilaa ja mennään sinne. Muuten jäädä kiltisti odottamaan C-laituripaikan vapautumista.

Itse koodi on aika selvää, mutta kuinka tarkastetaan A-laiturin vapaana oleminen? Sen voi tehdä kysymällä kuinka pitkä A-laiturin jono on, metodilla:

jonoA = len(laituriA.paikkoja.queue)

Sittenpä voimmekin koodata C-tyypin pyyntötoiminnot:

if  tyyppi == 'C':
       with laituriC.paikkoja.request() as pyynto:
            tulos = yield pyynto | env.timeout(5)
            if pyynto in tulos:
               print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi C-laituriin')
               yield env.process(laituritoiminnotC(env, laiva))
               print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
            else:
               print(kello(env),'C laituripaikkoja ei vapaana laivalle', laiva.get_tunnus())
               jonoA = len(laituriA.paikkoja.queue)
               if jonoA < Apaikkoja:
                   with laituriA.paikkoja.request() as  pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
                       yield env.process(laituritoiminnotA(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               else:
                   print(kello(env),'C Laivalle', laiva.get_tunnus(),' ei A-laituriakaan vapaanana. Jää odottamaan C-laituira')
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi C-laituriin')
                       yield env.process(laituritoiminnotC(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')

B-tyypin laivojen laituroinnin joudumme jakamaan kahteen osaan, koska alle 5000 tonnin kantavan aluksilla on lisämahdollisuus käyttää C-laitureita.

B-tyypin yli 5000 tonnin kanatavuuden laivojen ohjelma on käytännössä sama kuin C-tyypin, kunhan vain ’C vaihdetaan B’ksi’.


if tyyppi == 'B' and laiva.get_kantavuus() > 5000:
       with laituriB.paikkoja.request() as pyynto:
           tulos = yield pyynto | env.timeout(5)
           if pyynto in tulos:
               print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi B-laituriin')
               yield env.process(laituritoiminnotB(env, laiva))
               print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
           else:
               print(kello(env), 'B laituripaikkoja ei vapaana laivalle', laiva.get_tunnus())
               jonoA = len(laituriA.paikkoja.queue)
               if jonoA < Apaikkoja:
                   with laituriA.paikkoja.request() as  pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
                       yield env.process(laituritoiminnotA(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               else:
                   print(kello(env),'B Laivalle', laiva.get_tunnus(),' ei A-laituriakaan vapaanana. Jää odottamaan B-laituira')
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi C-laituriin')
                       yield env.process(laituritoiminnotB(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')

Pienenpien B-tyypin laivoille, pitää koodata lisätarkastus C-laiturien osalta. Se on vastaavanlainen kuin tarkastus A-laiturin käyttömahdollisuudesta.

if tyyppi == 'B'and laiva.get_kantavuus() <= 5000:
       with laituriB.paikkoja.request() as pyynto:
           tulos = yield pyynto | env.timeout(5)
           if pyynto in tulos:
               print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi B-laituriin')
               yield env.process(laituritoiminnotB(env, laiva))
               print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
           else:
               print(kello(env),'B laituripaikkoja ei vapaana laivalle', laiva.get_tunnus())
               jonoA = len(laituriA.paikkoja.queue)
               if jonoA < Apaikkoja:
                   with laituriA.paikkoja.request() as  pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
                       yield env.process(laituritoiminnotA(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               jonoC = len(laituriC.paikkoja.queue)
               if jonoC < Cpaikkoja:
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(), 'pääsi C-laituriin')
                       yield env.process(laituritoiminnotC(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               else:
                   print(kello(env),'B Laivalle', laiva.get_tunnus(),' ei A/C-laituriakaan vapaanana. Jää odottamaan B-laituira')
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi B-laituriin')
                       yield env.process(laituritoiminnotB(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')

Lopuksi meille ei jääkään muuta, kuin pitää huoli, että laivan tiedot tulee talteen otetuksi johonkin listamuuttujaan.

tiedot.append(laiva)

Muista määritellä listamuuttuja jossain välissä:

tiedot  = []

Mutta ei olla vielä valmiita, sillä laituripaikkapyynto – prosessissa kutsumme laivojen lastin purkavia ja lastaavia prosesseja laituritoiminnoiksi. Laivan purku- ja lastaus on samanlaista kaikille laivoille, joten ohjelman voi koodata niin, että jokaiselle laiturityypille on oma prosessi tai sitten yksi ja sama prosessi hoitaa kaikkia laiturityyppejä ottaen huomioon kunkin laiturityypin eri nopeudet.

Tässä on malli ’yksilöllisestä’ laituriprosessista:

def laituritoiminnotA(env, laiva):
   odotusaika = env.now - laiva.get_aika() '#Laituripaikan odotusaika
   laiva.set_odotus(odotusaika)
   purkunopeus = 200 '#Laiturikohtaiset purku ja lastausnopeudet
   lastausnopeus = 200
   purettavaa = laiva.get_purettavaa()
   lastattavaa = laiva.get_lastattavaa()
   purkuaika = round(purettavaa/purkunopeus,0) #Pyöristetään minuuteiksi
   lastausaika = round(lastattavaa/lastausnopeus,0)
   laituriaika = lastausaika + purkuaika '#Tämän ajan laiv'a vietää alaiturissa
   yield env.timeout(laituriaika) '#Ja sitten se aika vietetään
   satama_aika = odotusaika+laituriaika
   laiva.set_satama_aika(satama_aika) '#Pistetään satama-aika talteen
   print(kello(env),'Laiva', laiva.get_tunnus(),'odotusaika:',kello2(laiva.get_odotus()),'satama-aika', kello2(laiva.get_satama_aika()))

Vastaava prosessi pitää sitten koodata (copy/paste) B- ja C-laitureille niiden omilla purku- ja lastausnopeuksilla.

Kun laituritoiminnot on saatu koodattua, jää enää jäljellä pääohjelman ja tilastotietojen koodaaminen. Pääohjelmassa hyödynämme taas kelloajan asetusta Environment(init) – määrittelyssä (480 = klo 08:00 minuutteina)

env = simpy.Environment(480)
laituriA = Laituri(env, 'A', 1)
laituriB = Laituri(env, 'B', 2)
laituriC = Laituri(env, 'C',3)
env.process(GenLaivoja(env, laituriA, laituriB, laituriC))
env.run(until=1620)  '# Aika 1800 = klo 06:00 johon mennessä laivat ovat poistuneet satamasta  toivottavasti

Tilastotietojen käsittelyssä tarvitaan joukko summamuuttajia lastimäärien, odotusaikojen ja eri laivamäärien laskentaan.

purettavat = 0
lastattavat = 0
odotusajat=0
satama_aika=0
A_tyyppi=0
B_tyyppi=0
C_tyyppi=0

Sitten voidaankin käydä lista-muuttujan kimppuun ja ynnätä kunkin laivan tiedoista tarvittavat elementit summamuuttujiin ja lopuksi laskea ja tulostaa pyydetyt summan ja keskiarvot.

for i in range (0,len(tiedot)):
   laiva = tiedot[i]
   if laiva.get_tyyppi() =='A':
       A_tyyppi +=1
   if laiva.get_tyyppi() =='B':
       B_tyyppi +=1
   if laiva.get_tyyppi() =='C':
       C_tyyppi +=1
   purettavat = purettavat + laiva.get_purettavaa()
   lastattavat = lastattavat + laiva.get_lastattavaa()
   odotusajat = odotusajat + laiva.get_odotus()
   satama_aika=satama_aika + laiva.get_satama_aika()
summa = A_tyyppi+B_tyyppi+C_tyyppi
print('Päivän tilasto:')
print('Satamassa kävi A-tyypin laivoja', A_tyyppi, 'B-tyypin', B_tyyppi, 'ja   C-tyypin',C_tyyppi,'kappaletta')
print('Purettua lasti:', purettavat, 'ja lastattua:',lastattavat)
print('Keskimääräinen satama-aika:', int(satama_aika/summa),'josta odotusaikaa:', int(odotusajat/summa))

Lopuksi vielä koko koodi yhtenä kokonaisuutena.

import simpy
import random
tiedot = []
class Laiva:
   def __init__(self, tunnus, kantavuus, tyyppi, purettavaa, lastattavaa, aika):
       self.__tunnus = tunnus
       self.__kantavuus = kantavuus
       self.__tyyppi = tyyppi
       self.__purettavaa = purettavaa
       self.__lastattavaa = lastattavaa
       self.__aika=aika
       self.__odotusaika = 0
       self.__satama_aika = 0
   def get_tunnus(self):
       return self.__tunnus
   def get_kantavuus(self):
       return self.__kantavuus
   def get_tyyppi(self):
       return self.__tyyppi
   def get_purettavaa(self):
       return self.__purettavaa
   def get_lastattavaa(self):
       return self.__lastattavaa
   def get_aika(self):
       return self.__aika
   def get_satama_aika(self):
       return self.__satama_aika
   def get_odotus(self):
       return self.__odotusaika
   def set_odotus(self, odotusaika):
       self.__odotusaika = odotusaika
   def set_satama_aika(self,satama_aika):
       self.__satama_aika = satama_aika
lass Laituri:
   def __init__(self,env, tyyppi, paikkoja):
       self.env = env
       self.tyyppi = tyyppi
       self.paikkoja = simpy.Resource(env, paikkoja)
def kello(env):
   aika = env.now
   tunnit = int(aika/60)
   minuutit = int(aika - tunnit*60)
   if tunnit >= 24:
       tunnit = tunnit - 24
   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):
   tunnit = int(aika/60)
   minuutit = int(aika-tunnit*60)
   if tunnit >= 24:
       tunnit = tunnit - 24
   if minuutit < 10:
       smin = '0'+str(minuutit)
   else:
       smin =  str(minuutit)
   saika = str(tunnit)+':'+ smin
   return saika
'# A-tyypin laituri ottaa kaikki laivoja
'# B-tyypin laituri vain B tyypin laivoja
'# C-tyypin laituri  C-tyypin ja B-typin, kantavuus < 5000
def laituripyynto(env, laiva, laituriA, laituriB, laituriC):
   Apaikkoja=1
   Bpaikkoja=2
   Cpaikkoja=3
   tyyppi = laiva.get_tyyppi()
   if tyyppi == 'A':
       with laituriA.paikkoja.request() as  pyynto:
            yield pyynto
            print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
            yield env.process(laituritoiminnotA(env, laiva))
            print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
   if  tyyppi == 'C':
       with laituriC.paikkoja.request() as pyynto:
            tulos = yield pyynto | env.timeout(5)
            if pyynto in tulos:
               print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi C-laituriin')
               yield env.process(laituritoiminnotC(env, laiva))
               print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
            else:
               print(kello(env),'C laituripaikkoja ei vapaana laivalle', laiva.get_tunnus())
               jonoA = len(laituriA.paikkoja.queue)
               if jonoA < Apaikkoja:
                   with laituriA.paikkoja.request() as  pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
                       yield env.process(laituritoiminnotA(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               else:
                   print(kello(env),'C Laivalle', laiva.get_tunnus(),' ei A-laituriakaan vapaanana. Jää odottamaan C-laituira')
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi C-laituriin')
                       yield env.process(laituritoiminnotC(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
   if tyyppi == 'B' and laiva.get_kantavuus() > 5000:
       with laituriB.paikkoja.request() as pyynto:
           tulos = yield pyynto | env.timeout(5)
           if pyynto in tulos:
               print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi B-laituriin')
               yield env.process(laituritoiminnotB(env, laiva))
               print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
           else:
               print(kello(env), 'B laituripaikkoja ei vapaana laivalle', laiva.get_tunnus())
               jonoA = len(laituriA.paikkoja.queue)
               if jonoA < Apaikkoja:
                   with laituriA.paikkoja.request() as  pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
                       yield env.process(laituritoiminnotA(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               else:
                   print(kello(env),'B Laivalle', laiva.get_tunnus(),' ei A-laituriakaan vapaanana. Jää odottamaan B-laituira')
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi C-laituriin')
                       yield env.process(laituritoiminnotB(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
   if tyyppi == 'B'and laiva.get_kantavuus() <= 5000:
       with laituriB.paikkoja.request() as pyynto:
           tulos = yield pyynto | env.timeout(5)
           if pyynto in tulos:
               print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi B-laituriin')
               yield env.process(laituritoiminnotB(env, laiva))
               print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
           else:
               print(kello(env),'B laituripaikkoja ei vapaana laivalle', laiva.get_tunnus())
               jonoA = len(laituriA.paikkoja.queue)
               if jonoA < Apaikkoja:
                  with laituriA.paikkoja.request() as  pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi A-laituriin')
                       yield env.process(laituritoiminnotA(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               jonoC = len(laituriC.paikkoja.queue)
               if jonoC < Cpaikkoja:
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(), 'pääsi C-laituriin')
                       yield env.process(laituritoiminnotC(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
               else:
                   print(kello(env),'B Laivalle', laiva.get_tunnus(),' ei A/C-laituriakaan vapaanana. Jää odottamaan B-laituira')
                   with laituriC.paikkoja.request() as pyynto:
                       yield pyynto
                       print(kello(env),'Laiva', laiva.get_tunnus(),'pääsi B-laituriin')
                       yield env.process(laituritoiminnotB(env, laiva))
                       print(kello(env),'Laiva',laiva.get_tunnus(),'lähti satamasta')
   tiedot.append(laiva)
def laituritoiminnotA(env, laiva):
   odotusaika = env.now - laiva.get_aika()
   #print(laiva.get_aika())
   laiva.set_odotus(odotusaika)
   purkunopeus = 200
   lastausnopeus = 200
   purettavaa = laiva.get_purettavaa()
   lastattavaa = laiva.get_lastattavaa()
   purkuaika = round(purettavaa/purkunopeus,0)
   lastausaika = round(lastattavaa/lastausnopeus,0)
   laituriaika = lastausaika + purkuaika
   yield env.timeout(laituriaika)
   satama_aika = odotusaika+laituriaika
   laiva.set_satama_aika(satama_aika)
   print(kello(env),'Laiva', laiva.get_tunnus(),'odotusaika:',kello2(laiva.get_odotus()),'satama-aika', kello2(laiva.get_satama_aika()))
def laituritoiminnotB(env, laiva):
   odotusaika = env.now - laiva.get_aika()
   laiva.set_odotus(odotusaika)
   purkunopeus = 100
   lastausnopeus = 75
   purettavaa = laiva.get_purettavaa()
   lastattavaa = laiva.get_lastattavaa()
   purkuaika = round(purettavaa/purkunopeus,0)
   lastausaika = round(lastattavaa/lastausnopeus,0)
   laituriaika = lastausaika + purkuaika
   yield env.timeout(laituriaika)
   satama_aika = odotusaika+laituriaika
   laiva.set_satama_aika(satama_aika)
   print(kello(env), 'Laiva', laiva.get_tunnus(),'odotusaika:',kello2(laiva.get_odotus()),'satama-aika', kello2(laiva.get_satama_aika()))
def laituritoiminnotC(env, laiva):
   odotusaika = env.now - laiva.get_aika()
   laiva.set_odotus(odotusaika)
   purkunopeus = 75
   lastausnopeus = 60
   purettavaa = laiva.get_purettavaa()
   lastattavaa = laiva.get_lastattavaa()
   purkuaika = round(purettavaa/purkunopeus,0)
   lastausaika = round(lastattavaa/lastausnopeus,0)
   laituriaika = lastausaika + purkuaika
   yield env.timeout(laituriaika)
   satama_aika = odotusaika+laituriaika
   laiva.set_satama_aika(satama_aika)
   print(kello(env),'Laiva', laiva.get_tunnus(),'odotusaika:',kello2(laiva.get_odotus()),'satama-aika', kello2(laiva.get_satama_aika()))
def GenLaivoja(env,laituriA, laituriB, laituriC):
   tunnus = 0
   while True:
       if env.now <= 1200:
           kantavuus = 0
           aika=env.now
           tyyppi = random.choice(['A','A','A','B','B','B','B','B','C','C','C','C'])
           if tyyppi == 'A':
               purettavaa = 1000*random.randint(8,12)
               lastattavaa = 1000*random.randint(4,12)
           if tyyppi == 'B':
               kantavuus=random.randint(2,8)
               purettavaa = 1000*random.randint(2,kantavuus)
               lastattavaa = 1000*random.randint(2,kantavuus)
               kantavuus=1000*kantavuus
           if tyyppi == 'C':
               purettavaa = 1000*random.randint(2,5)
               lastattavaa = 1000*random.randint(2,5)
           laiva = Laiva(tunnus, kantavuus, tyyppi, purettavaa, lastattavaa, aika)
           print(kello(env),'Laiva', tunnus, ',tyyppi;',tyyppi, ',kantavuus:', kantavuus, ',purettavaa:', purettavaa,
                 ',lastettavaa:', lastattavaa, 'saapuu')
           env.process(laituripyynto(env, laiva, laituriA, laituriB, laituriC))
           yield env.timeout(random.randint(10,30))
           tunnus=tunnus+1
       else:
           break
'#Pääohjelma
env = simpy.Environment(480)
laituriA = Laituri(env, 'A', 1)
laituriB = Laituri(env, 'B', 2)
laituriC = Laituri(env, 'C',3)
env.process(GenLaivoja(env, laituriA, laituriB, laituriC))
env.run(until=1800)
purettavat = 0
lastattavat = 0
odotusajat=0
satama_aika=0
A_tyyppi=0
B_tyyppi=0
C_tyyppi=0
for i in range (0,len(tiedot)):
   laiva = tiedot[i]
   if laiva.get_tyyppi() =='A':
       A_tyyppi +=1
   if laiva.get_tyyppi() =='B':
       B_tyyppi +=1
   if laiva.get_tyyppi() =='C':
       C_tyyppi +=1
   purettavat = purettavat + laiva.get_purettavaa()
   lastattavat = lastattavat + laiva.get_lastattavaa()
   odotusajat = odotusajat + laiva.get_odotus()
   satama_aika=satama_aika + laiva.get_satama_aika()
summa = A_tyyppi+B_tyyppi+C_tyyppi
print('Päivän tilasto:')
print('Satamassa kävi A-tyypin laivoja', A_tyyppi, 'B-tyypin', B_tyyppi, 'ja C-tyypin',C_tyyppi,'kappaletta')
print('Purettua lasti:', purettavat, 'ja lastattua:',lastattavat)
print('Keskimääräinen satama-aika:', int(satama_aika/summa),'josta odotusaikaa:', int(odotusajat/summa))