environment.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. ### DO NOT EDIT THIS FILE ###
  2. from pypdevs.DEVS import AtomicDEVS
  3. import random
  4. import dataclasses
  5. # 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.
  6. class Ship:
  7. def __init__(self, size, creation_time):
  8. self.size = size
  9. self.creation_time = creation_time
  10. # useful in verbose mode:
  11. def __repr__(self):
  12. return f"Ship(size={self.size},created={self.creation_time})"
  13. @dataclasses.dataclass
  14. class GeneratorState:
  15. current_time: float
  16. time_until_next_ship: float
  17. to_generate: int
  18. random: random.Random
  19. def __init__(self, seed=0, gen_num=1000):
  20. self.current_time = 0.0 # for statistics only
  21. self.time_until_next_ship = 0.0
  22. self.to_generate = gen_num
  23. self.random = random.Random(seed)
  24. class Generator(AtomicDEVS):
  25. def __init__(self,
  26. seed=0, # random seed
  27. lambd=1.0/60.0, # how often to generate a ship - in this example, once per minute
  28. 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.
  29. gen_num=1000, # number of ships total to generate
  30. ):
  31. super().__init__("Generator")
  32. # State (for everything that is mutable)
  33. self.state = GeneratorState(seed=seed, gen_num=gen_num)
  34. # I/O
  35. self.out_ship = self.addOutPort("out_event")
  36. # Parameters (read-only)
  37. self.lambd = lambd
  38. self.gen_types = gen_types
  39. def timeAdvance(self):
  40. return self.state.time_until_next_ship
  41. def outputFnc(self):
  42. size = self.state.random.choice(self.gen_types) # uniformly sample from gen_types
  43. # watch out: outputFnc is called *before* intTransition!
  44. creation = self.state.current_time + self.state.time_until_next_ship
  45. return { self.out_ship: Ship(size, creation) }
  46. def intTransition(self):
  47. self.state.current_time += self.state.time_until_next_ship
  48. self.state.to_generate -= 1
  49. if self.state.to_generate > 0:
  50. self.state.time_until_next_ship = self.state.random.expovariate(self.lambd)
  51. else:
  52. # stop generating
  53. self.state.time_until_next_ship = float('inf')
  54. return self.state
  55. @dataclasses.dataclass
  56. class SinkState:
  57. current_time: float
  58. ships: list
  59. def __init__(self):
  60. self.current_time = 0.0
  61. self.ships = []
  62. class Sink(AtomicDEVS):
  63. def __init__(self):
  64. super().__init__("Sink")
  65. self.state = SinkState()
  66. self.in_ships = self.addInPort("in_ships")
  67. def extTransition(self, inputs):
  68. self.state.current_time += self.elapsed
  69. if self.in_ships in inputs:
  70. ships = inputs[self.in_ships]
  71. for ship in ships:
  72. ship.finished_time = self.state.current_time
  73. # amount of time spent in the system:
  74. ship.queueing_duration = ship.finished_time - ship.creation_time
  75. self.state.ships.extend(ships)
  76. return self.state
  77. ### DO NOT EDIT THIS FILE ###