mosis24DEVS/assignment/environment.py
2024-12-13 14:28:07 +01:00

98 lines
3.3 KiB
Python

### DO NOT EDIT THIS FILE ###
from pypdevs.DEVS import AtomicDEVS
import random
import dataclasses
# The reason for annotating the *State-classes as 'dataclass', is because this automatically generates a nice __repr__-function, so that if the simulator is set to verbose, you can actually see what the state is.
class Ship:
def __init__(self, size, creation_time):
self.size = size
self.creation_time = creation_time
# useful in verbose mode:
def __repr__(self):
return f"Ship(size={self.size},created={self.creation_time})"
@dataclasses.dataclass
class GeneratorState:
current_time: float
time_until_next_ship: float
to_generate: int
random: random.Random
def __init__(self, seed=0, gen_num=1000):
self.current_time = 0.0 # for statistics only
self.time_until_next_ship = 0.0
self.to_generate = gen_num
self.random = random.Random(seed)
class Generator(AtomicDEVS):
def __init__(self,
seed=0, # random seed
lambd=1.0/60.0, # how often to generate a ship - in this example, once per minute
gen_types=[1,1,2], # ship sizes to generate, will be sampled uniformly - in this example, size 1 is twice as likely as size 2.
gen_num=1000, # number of ships total to generate
):
super().__init__("Generator")
# State (for everything that is mutable)
self.state = GeneratorState(seed=seed, gen_num=gen_num)
# I/O
self.out_ship = self.addOutPort("out_event")
# Parameters (read-only)
self.lambd = lambd
self.gen_types = gen_types
def timeAdvance(self):
return self.state.time_until_next_ship
def outputFnc(self):
size = self.state.random.choice(self.gen_types) # uniformly sample from gen_types
# watch out: outputFnc is called *before* intTransition!
creation = self.state.current_time + self.state.time_until_next_ship
return { self.out_ship: Ship(size, creation) }
def intTransition(self):
self.state.current_time += self.state.time_until_next_ship
self.state.to_generate -= 1
if self.state.to_generate > 0:
self.state.time_until_next_ship = self.state.random.expovariate(self.lambd)
else:
# stop generating
self.state.time_until_next_ship = float('inf')
return self.state
@dataclasses.dataclass
class SinkState:
current_time: float
ships: list
def __init__(self):
self.current_time = 0.0
self.ships = []
class Sink(AtomicDEVS):
def __init__(self):
super().__init__("Sink")
self.state = SinkState()
# On this input port, the Sink expects to receive a *list* of Ships. This is because a Lock can contain more than one Ship, and the Lock can send them all at once to the Sink (with a single event).
self.in_ships = self.addInPort("in_ships")
def extTransition(self, inputs):
self.state.current_time += self.elapsed
if self.in_ships in inputs:
ships = inputs[self.in_ships]
for ship in ships:
ship.finished_time = self.state.current_time
# amount of time spent in the system:
ship.queueing_duration = ship.finished_time - ship.creation_time
self.state.ships.extend(ships)
return self.state
### DO NOT EDIT THIS FILE ###