def test_pool_cancel(env): pool = Pool(env) event_cancel = pool.get(2) event_cancel.cancel() event_full = pool.when_full() event_full.cancel() event_any = pool.when_any() event_any.cancel() event_new = pool.when_new() event_new.cancel() env.run() assert pool.level == 0 assert not event_cancel.triggered assert not event_full.triggered assert not event_any.triggered assert not event_new.triggered
class TankerTruck(Component): """Tanker trucks carry fuel to gas stations. Each tanker truck has a queue of gas stations it must visit. When the truck's tank becomes empty, it must go refill itself. """ base_name = 'truck' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.pump_rate = self.env.config.get('tanker.pump_rate', 10) self.avg_travel = self.env.config.get('tanker.travel_time', 600) tank_capacity = self.env.config.get('tanker.capacity', 200) self.tank = Pool(self.env, tank_capacity) # This auto_probe() call uses the self.tank Pool get/put hooks so that # whenever it's level changes, the new level is noted in the log. self.auto_probe('tank', log={}) # The parent TankerCompany enqueues instructions to this queue. self._instructions = Queue(self.env) # Declare a persistant process to be started at simulation-time. self.add_process(self._dispatch_loop) def dispatch(self, gas_station, done_event): """Append dispatch instructions to the truck's queue.""" return self._instructions.put((gas_station, done_event)) def _dispatch_loop(self): """This is the tanker truck's main behavior. Travel, pump, refill...""" while True: if not self.tank.level: self.info('going for refill') # Desmod simulation environments come equipped with a # random.Random() instance seeded based on the 'sim.seed' # configuration key. travel_time = self.env.rand.expovariate(1 / self.avg_travel) yield self.env.timeout(travel_time) self.info('refilling') pump_time = self.tank.capacity / self.pump_rate yield self.env.timeout(pump_time) yield self.tank.put(self.tank.capacity) self.info( f'refilled {self.tank.capacity}L in {pump_time:.0f}s') gas_station, done_event = yield self._instructions.get() self.info(f'traveling to {gas_station.name}') travel_time = self.env.rand.expovariate(1 / self.avg_travel) yield self.env.timeout(travel_time) self.info(f'arrived at {gas_station.name}') while self.tank.level and (gas_station.reservoir.level < gas_station.reservoir.capacity): yield self.env.timeout(1 / self.pump_rate) yield gas_station.reservoir.put(1) yield self.tank.get(1) self.info('done pumping') done_event.succeed()
class TankerTruck(Component): """Tanker trucks carry fuel to gas stations. Each tanker truck has a queue of gas stations it must visit. When the truck's tank becomes empty, it must go refill itself. """ base_name = 'truck' def __init__(self, *args, **kwargs): super(TankerTruck, self).__init__(*args, **kwargs) self.pump_rate = self.env.config.get('tanker.pump_rate', 10) self.avg_travel = self.env.config.get('tanker.travel_time', 600) tank_capacity = self.env.config.get('tanker.capacity', 200) self.tank = Pool(self.env, tank_capacity) # This auto_probe() call uses the self.tank Pool get/put hooks so that # whenever it's level changes, the new level is noted in the log. self.auto_probe('tank', log={}) # The parent TankerCompany enqueues instructions to this queue. self._instructions = Queue(self.env) # Declare a persistant process to be started at simulation-time. self.add_process(self._dispatch_loop) def dispatch(self, gas_station, done_event): """Append dispatch instructions to the truck's queue.""" return self._instructions.put((gas_station, done_event)) def _dispatch_loop(self): """This is the tanker truck's main behavior. Travel, pump, refill...""" while True: if not self.tank.level: self.info('going for refill') # Desmod simulation environments come equipped with a # random.Random() instance seeded based on the 'sim.seed' # configuration key. travel_time = self.env.rand.expovariate(1 / self.avg_travel) yield self.env.timeout(travel_time) self.info('refilling') pump_time = self.tank.capacity / self.pump_rate yield self.env.timeout(pump_time) yield self.tank.put(self.tank.capacity) self.info('refilled {}L in {:.0f}s'.format( self.tank.capacity, pump_time)) gas_station, done_event = yield self._instructions.get() self.info('traveling to {}'.format(gas_station.name)) travel_time = self.env.rand.expovariate(1 / self.avg_travel) yield self.env.timeout(travel_time) self.info('arrived at {}'.format(gas_station.name)) while self.tank.level and (gas_station.reservoir.level < gas_station.reservoir.capacity): yield self.env.timeout(1 / self.pump_rate) yield gas_station.reservoir.put(1) yield self.tank.get(1) self.info('done pumping') done_event.succeed()
class GasStation(Component): """A gas station has a fuel reservoir shared among several fuel pumps. The gas station has a traffic generator process that causes cars to arrive to fill up their tanks. As the cars fill up, the reservoir's level goes down. When the level goes below a critical threshold, the gas station makes a request to the tanker company for a tanker truck to refill the reservoir. """ base_name = 'station' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) config = self.env.config self.add_connections('tanker_company') self.arrival_interval = config.get('gas_station.arrival_interval', 60) station_capacity = config.get('gas_station.capacity', 200) self.reservoir = Pool(self.env, capacity=station_capacity, init=station_capacity) self.auto_probe('reservoir', log={}) threshold_pct = config.get('gas_station.threshold_pct', 10) self.reservoir_low_water = threshold_pct * station_capacity / 100 self.pump_rate = config.get('gas_station.pump_rate', 2) num_pumps = config.get('gas_station.pumps', 2) self.fuel_pumps = Resource(self.env, capacity=num_pumps) self.auto_probe('fuel_pumps', log={}) self.car_capacity = config.get('car.capacity', 50) self.car_level_range = config.get('car.level', [5, 25]) # A gas station has two persistent processes. One to monitor the # reservoir level and one that models the arrival of cars at the # station. Desmod starts these processes before simulation phase. self.add_processes(self._monitor_reservoir, self._traffic_generator) @property def reservoir_pct(self): return self.reservoir.level / self.reservoir.capacity * 100 def _monitor_reservoir(self): """Periodically monitor reservoir level. The a request is made to the tanker company when the reservoir falls below a critical threshold. """ while True: yield self.reservoir.when_at_most(self.reservoir_low_water) done_event = self.env.event() yield self.tanker_company.request_truck(self, done_event) yield done_event def _traffic_generator(self): """Model the sporadic arrival of cars to the gas station.""" for i in count(): interval = self.env.rand.expovariate(1 / self.arrival_interval) yield self.env.timeout(interval) self.env.process(self._car(i)) def _car(self, i): """Model a car transacting fuel.""" with self.fuel_pumps.request() as pump_req: self.info(f'car{i} awaiting pump') yield pump_req self.info(f'car{i} at pump') car_level = self.env.rand.randint(*self.car_level_range) amount = self.car_capacity - car_level t0 = self.env.now for _ in range(amount): yield self.reservoir.get(1) yield self.env.timeout(1 / self.pump_rate) pump_time = self.env.now - t0 self.info(f'car{i} pumped {amount}L in {pump_time:.0f}s')
class GasStation(Component): """A gas station has a fuel reservoir shared among several fuel pumps. The gas station has a traffic generator process that causes cars to arrive to fill up their tanks. As the cars fill up, the reservoir's level goes down. When the level goes below a critical threshold, the gas station makes a request to the tanker company for a tanker truck to refill the reservoir. """ base_name = 'station' def __init__(self, *args, **kwargs): super(GasStation, self).__init__(*args, **kwargs) config = self.env.config self.add_connections('tanker_company') self.arrival_interval = config.get('gas_station.arrival_interval', 60) station_capacity = config.get('gas_station.capacity', 200) self.reservoir = Pool( self.env, capacity=station_capacity, init=station_capacity ) self.auto_probe('reservoir', log={}) threshold_pct = config.get('gas_station.threshold_pct', 10) self.reservoir_low_water = threshold_pct * station_capacity / 100 self.pump_rate = config.get('gas_station.pump_rate', 2) num_pumps = config.get('gas_station.pumps', 2) self.fuel_pumps = Resource(self.env, capacity=num_pumps) self.auto_probe('fuel_pumps', log={}) self.car_capacity = config.get('car.capacity', 50) self.car_level_range = config.get('car.level', [5, 25]) # A gas station has two persistent processes. One to monitor the # reservoir level and one that models the arrival of cars at the # station. Desmod starts these processes before simulation phase. self.add_processes(self._monitor_reservoir, self._traffic_generator) @property def reservoir_pct(self): return self.reservoir.level / self.reservoir.capacity * 100 def _monitor_reservoir(self): """Periodically monitor reservoir level. The a request is made to the tanker company when the reservoir falls below a critical threshold. """ while True: yield self.reservoir.when_at_most(self.reservoir_low_water) done_event = self.env.event() yield self.tanker_company.request_truck(self, done_event) yield done_event def _traffic_generator(self): """Model the sporadic arrival of cars to the gas station.""" for i in count(): interval = self.env.rand.expovariate(1 / self.arrival_interval) yield self.env.timeout(interval) self.env.process(self._car(i)) def _car(self, i): """Model a car transacting fuel.""" with self.fuel_pumps.request() as pump_req: self.info('car{} awaiting pump'.format(i)) yield pump_req self.info('car{} at pump'.format(i)) car_level = self.env.rand.randint(*self.car_level_range) amount = self.car_capacity - car_level t0 = self.env.now for _ in range(amount): yield self.reservoir.get(1) yield self.env.timeout(1 / self.pump_rate) pump_time = self.env.now - t0 self.info('car{} pumped {}L in {:.0f}s'.format( i, amount, pump_time))