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 = Container(self.env, capacity=station_capacity, init=station_capacity) self.auto_probe('reservoir', log={}) self.threshold_pct = config.get('gas_station.threshold_pct', 10) 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)
def cross_sim(): """ cross module test: Cross 模块测试单元 """ env = Environment() INPUT_QUEUE_ID.extend( [''.join([MACHINE_ID, '_in', str(i)]) for i in range(NUM_PORT_IN)] ) OUTPUT_QUEUE_ID.extend( [''.join([MACHINE_ID, '_out', str(i)]) for i in range(NUM_PORT_OUT)] ) for id in INPUT_QUEUE_ID: INPUT_QUEUE_DIC.update({id: PriorityStore(env=env)}) for id in OUTPUT_QUEUE_ID: OUTPUT_QUEUE_DIC.update({id: PriorityStore(env=env)}) generator_queue_res = Resource(env=env, capacity=QUEUE_RES) generator_package_res = Resource(env=env, capacity=PACKAGE_RES) packages(env=env, generator_package_res=generator_package_res, generator_queue_res=generator_queue_res) hospital = Hospital(env=env,id_h=MACHINE_ID, hospital_capacity=NUM_PEOPLE, input_dic=INPUT_QUEUE_DIC, output_dic=OUTPUT_QUEUE_DIC) env.run()
def __init__(self, data_series: EventSeries, pkg_process_delay: int = 0, **kwargs): self.pkg_process_delay = pkg_process_delay self.data_series = data_series super().__init__(**kwargs) self.link_queues = {} self.router_queues = {} for router_id in self.conn_graph.nodes: self.link_queues[router_id] = {} for _, nbr in self.conn_graph.edges(router_id): self.link_queues[router_id][nbr] = Resource(self.env, capacity=1) self.router_queues[router_id] = Resource(self.env, capacity=1)
def __init__(self, env: Environment, name: str, block_capacity=float("inf")): self.name = name self.env: Environment = env self.overall_count_in = 0 on_enter_or_exit_method_callable = Callable[ [Entity, Optional[Block], Optional[Block]], None ] self.do_on_enter_list: List[on_enter_or_exit_method_callable] = [] self.do_on_exit_list: List[on_enter_or_exit_method_callable] = [] self.entities: List[Entity] = [] self.successors: List[Block] = [] self.block_resource = Resource(env=env, capacity=block_capacity) self.state_manager = StateManager(self.on_block_state_change) self.env.process(self.late_state_evaluation())
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 = Container(self.env, capacity=station_capacity, init=station_capacity) self.auto_probe('reservoir', log={}) self.threshold_pct = config.get('gas_station.threshold_pct', 10) 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)
def create(self, env: Environment, fn: FunctionContainer) -> FunctionSimulator: workers = int(fn.labels['workers']) queue = Resource(env=env, capacity=workers) return InterferenceAwarePythonHttpSimulator( queue, linear_queue_fet_increase, fn, self.fn_characterizations[fn.image])
def __init__(self, env): self.env = env self.generator_process = env.process(self.generate_request(env)) self.resource_list = [ Resource(env, capacity=1) for _ in range(defines.CHARGING_STATIONS_COUNT) ]
def create(self, env: Environment, fn: FunctionDefinition) -> FunctionSimulator: workers = int(fn.labels['workers']) queue = Resource(env=env, capacity=workers) return InterferenceAwarePythonHttpSimulator(queue, linear_queue_fet_increase, fn)
def __init__(self, env, duration, inspectors=float('inf'), officers=float('inf'), budget=float('inf'), max_loan=float('inf'), min_credit=0, debt_income_ratio=0.2, loan_term=30.0, interest_rate=0.04, declaration=0, deadline=60): """Initiate SBA real property loan recovery program. Keyword Arguments: env -- simpy.Envionment() object duration -- io.ProbabilityDistribution() object inspectors -- Integer, indicating number of building inspectors assigned to the programs officers -- Number of program staff that reviews and approves loan applications budget -- Integer or float, indicating the initial budget available from the recovery program. max_loan -- The maximum amount ($) of loan that any one entity can receive debt_income_ratio -- Monthly SBA loan payment / entity income ; used to estimate loan amount. min_credit -- A FICO-like credit score used as threshold for approving loan. declaration -- A value indicating how many days after the event a federal disaster was declared. deadline -- A value indicating how many days after the federal disaster declaration was made the applications must be submitted. """ FinancialRecoveryProgram.__init__(self, env, duration, budget) # Define staff/personnel specific to this class self.officers = Resource(self.env, capacity=officers) self.inspectors = Resource(self.env, capacity=inspectors) # New attributes self.min_credit = min_credit self.debt_income_ratio = debt_income_ratio self.loan_term = loan_term # years self.max_loan = max_loan self.interest_rate = interest_rate # annual rate self.deadline = deadline self.declaration = declaration
def _attach_resource_users(resource: simpy.Resource, callbacks: ProbeCallbacks) -> None: def make_wrapper(func): @wraps(func) def wrapper(*args, **kwargs): old_users = len(resource.users) ret = func(*args, **kwargs) new_users = len(resource.users) if new_users != old_users: for callback in callbacks: callback(new_users) return ret return wrapper resource._do_get = make_wrapper(resource._do_get) # type: ignore resource._do_put = make_wrapper(resource._do_put) # type: ignore
def _attach_resource_queue(resource: simpy.Resource, callbacks: ProbeCallbacks) -> None: def make_wrapper(func): @wraps(func) def wrapper(*args, **kwargs): old_queue = len(resource.queue) ret = func(*args, **kwargs) new_queue = len(resource.queue) if new_queue != old_queue: for callback in callbacks: callback(new_queue) return ret return wrapper resource.request = make_wrapper(resource.request) # type: ignore resource._trigger_put = make_wrapper(resource._trigger_put) # type: ignore
def __init__(self, network, S, speed, outbuf_cap, environment): super(Server, self).__init__() self.quality_levels = [1.5*S, 4.0*S, 7.5*S, 12*S, 68*S] self.network = network self.S = S self.outbuf_cap = outbuf_cap self.outbuf_size = 0 self.env = environment self.speed = speed # Mbps self.network_interface = Resource(self.env, 1) self.buf_sz = [] self.time = [] self.nclientsN = 0 self.nclients = [] self.time_clients = [] self.sender = None self.data = None
def __init__(self, id=None, name=None, **kw): Entity.__init__(self, id=id, name=name) self.Res = Resource(self.capacity) # dimension data self.width = 2.0 self.height = 2.0 self.lenght = 2.0
def process(kwargs: SystemArgs): event_store = kwargs['EVENT_STORE'] env = kwargs['ENV'] kitchen_layout = kwargs['WORLD'].component_for_entity(1, KitchenLayout) # Creates a resource to manage cooks concurrently kitchen_layout.available_cooks = Resource(env, kitchen_layout.personnel) # For areas that have a max_capacity, create a resource to manage it for k, v in kitchen_layout.areas.items(): if v.max_resources > 0: v.resource = Resource(env, v.max_resources) # Sink area needs an extra info - amount of dishes piling up kitchen_layout.areas['Sink'].dishes = 0 handlers = { IncomingOrderEventTag: handle_incoming_order, CompleteOrderEventTag: handle_complete_order } while True: event = yield event_store.get(filter_function) handlers[event.type](event.payload, kwargs)
def resource_run(env): begin_t = time.time() car_arrive_gap = 0 bcs = Resource(env, capacity=2) for i in range(6): car_arrive_time = car_arrive_gap * i env.process(car_req(env, 'Car %d' % i, bcs, car_arrive_time, 5)) env.run() run_t = time.time() - begin_t print(run_t)
def __init__(self, env, duration, staff=float('inf'), budget=float('inf')): """Initiate financial recovery program attributes. Keyword Arguments: env -- simpy.Envionment() object duration -- io.ProbabilityDistribution() object staff -- Integer, indicating number of staff assigned to the programs budget -- Integer or float, indicating the initial budget available from the recovery program. Attribute Changes: self.staff -- A simpy.Resource() object with a capacity == staff arg self.budget -- A simpy.Container() object with a initial value == budget arg self.duration -- A function that is used to calculate random durations for the program process """ self.env = env self.staff = Resource(self.env, capacity=staff) self.budget = Container(self.env, init=budget) self.duration = duration
def car(env,name,bcs:simpy.Resource,driving_time,charge_duration): # simulate driving to the BCS yield env.timeout(driving_time) print('%s arriving at %d' % (name, env.now)) #Request one of its charging spots with bcs.request() as req: yield req # charge the battery print('%s starting to charge at %s' % (name, env.now)) yield env.timeout(charge_duration) print('%s leaving the bcs at %s' % (name, env.now))
def main_run(): """""" env = Environment() priority_store = PriorityStore(env=env) res_port = Resource(env=env, capacity=2) for i in range(NUM_PACKAGE): env.process( put_package_queue(env=env, id=i, res=res_port, queue=priority_store)) #env.process(get_package(env, priority_store)) env.run()
def __init__(self, env, cp_params): self.env = env self.in_pipe = Store(self.env) # TODO: define capacity in cp_params self.pending_jobs = {} self.response_times = [] self.interarrivals = [] self.queue_times = [] self.num_cores = cp_params['num_cores'] self.used_cores = 0 self.core_speed = random_number(cp_params['core_speed']) self.cores = Resource(self.num_cores) self.name = cp_params['name'] self.jobs_completed = 0 self.sim_components = None self.data_res = [] self.idle_time = 0 self.time_last_arrival = None
def __init__(self, address: str, radio: Callable[[str], Generator[Event, Any, Any]], env: Environment) -> None: self.address = address self._radio = radio self.env = env # noinspection PyArgumentEqualDefault # 'capacity' sets the quantity of packets that can be sent at the same time # 'capacity=1' sets a more realistic model with queue delay for congested networks # 'capacity=9999999' sets a less realistic model without queue delay for congested networks self._output_queue = Resource(env, capacity=1) # List to save messages in format: (timestamp, message) # Used to calculate performance self._received_messages = [] self._output_queue_messages = [] self._message_sending = [] self._message_sent = []
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.cashier = Cashier(self) self.customer_queue = Resource(self.env) self.auto_probe('customer_queue', trace_queue=True, vcd={'init': 0}) feed_capacity = self.env.config['checkout.feed_capacity'] self.feed_belt = Queue(self.env, capacity=feed_capacity) self.auto_probe('feed_belt', vcd={}) bag_area_capacity = self.env.config['checkout.bag_area_capacity'] self.bag_area = Queue(self.env, capacity=bag_area_capacity) self.auto_probe('bag_area', vcd={}) self.baggers = Container(self.env) self.auto_probe('baggers', vcd={})
def cross_sim(): """ cross module test: Cross 模块测试单元 """ env = Environment() INPUT_QUEUE_DIC.update({ 'x1_in1': PriorityStore(env=env), 'x1_in2': PriorityStore(env=env) }) generator_queue_res = Resource(env=env, capacity=NUM_PORT_ABLE) env.process( machine_package(env=env, generator_queue_res=generator_queue_res)) OUTPUT_QUEUE = PriorityStore(env) Cross(env=env, id_x=CROSS_ID, input_dic=INPUT_QUEUE_DIC, out_put=OUTPUT_QUEUE) env.run(until=50)
class Server(object): """Documentation for Server """ def __init__(self, network, S, speed, outbuf_cap, environment): super(Server, self).__init__() self.quality_levels = [1.5*S, 4.0*S, 7.5*S, 12*S, 68*S] self.network = network self.S = S self.outbuf_cap = outbuf_cap self.outbuf_size = 0 self.env = environment self.speed = speed # Mbps self.network_interface = Resource(self.env, 1) self.buf_sz = [] self.time = [] self.nclientsN = 0 self.nclients = [] self.time_clients = [] self.sender = None self.data = None def incoming_packet(self, sender, data): #print("packet received from client: ", self.incoming_data) self.sender = sender self.data = data quality = self.data.content timestamp = self.data.timestamp if self.outbuf_size + self.quality_levels[quality] > self.outbuf_cap: yield self.env.process(self.network.send(self, self.sender, Data(0, "ERROR", timestamp))) else: yield self.env.process(self.process(self.sender, quality, timestamp)) def process(self, sender, quality, timestamp): self.outbuf_size += self.quality_levels[quality] self.buf_sz.append(self.outbuf_size) self.time.append(self.env.now) with self.network_interface.request() as req: yield req yield self.env.process(self.network.send(self, sender, Data(self.quality_levels[quality], "", timestamp, quality))) self.outbuf_size -= self.quality_levels[quality]
def __init__(self, env, id, area, perimeter, can_see, replenish_rate=0.1): """ :param env: :type env: Environment :type id: int :param area: :type area: float :param perimeter: :type perimeter: float :param can_see: :type can_see: dict[int, float] """ r_cap = int(area / 0.17384) + 1 # r_cap = MAX_CAPACITY perimeter = int(perimeter * DEG_TO_KM) + 1 self.env = env self.id = id self.area = area self.perimeter = perimeter self._can_see = can_see self.can_see, self.probs = self.normalise(can_see) self.replenish_rate = replenish_rate self.r = Resource(self.env, capacity=r_cap) self.c = Container(self.env, capacity=perimeter, init=perimeter) self.action = self.env.process(self.replenish()) self._populations = set() self._pop = None self._pop_calc_time = None self._density = None self._density_calc_time = None self._capacity = None self._capacity_calc_time = None
def car(env: simpy.Environment, name: str, gas_station: simpy.Resource, fuel_pump: simpy.Container) -> Generator: """A car arrives at the gas station for refueling. It requests one of the gas station's fuel pumps and tries to get the desired amount of gas from it. If the stations reservoir is depleted, the car has to wait for the tank truck to arrive. """ fuel_tank_level = random.randint(*FUEL_TANK_LEVEL) print(f"{name} arriving at gas station at {env.now:.1f}") with gas_station.request() as req: start = env.now # Request one of the gas pumps yield req # Get the required amount of fuel liters_required = FUEL_TANK_SIZE - fuel_tank_level yield fuel_pump.get(liters_required) # The "actual" refueling process takes some time yield env.timeout(liters_required / REFUELING_SPEED) print(f"{name} finished refueling in {env.now - start:.1f} seconds")
or (parameters["Time at work [min]"] > 15.0 * 60) or (parameters["Battery capacity [kwh]"] < 5.0) or (parameters["Battery capacity [kwh]"] > 100.0) or ((2 * parameters["Distance to workplace [km]"]) > parameters["Range [km]"])): return (False) return (True) n_cars = 30000 env = simpy.Environment() n_stations = 200 globalEnergyGiant = EnergyProvider() # Charging stations publicChargingStation = Resource(env, capacity=n_stations) cars = [] i = 0 while i < n_cars: bat_ctrl = BatteryController(env, globalEnergyGiant) car = REVCar(env, bat_ctrl) if sane(car): cars.append(car) i += 1 stats = describe(cars) env.run(60 * 24 - 1)
""" simpy tutorial process interaction part """ from simpy import Environment, Resource def car(env, name, bcs, driving_time, charge_duration): "battery charging station (BCS) is bsc resource " yield env.timeout(driving_time) print("{name} arrive at battery charging station at {time}".format( name=name, time=env.now)) with bcs.request() as req: yield req print('%s starting to charge at %s' % (name, env.now)) yield env.timeout(charge_duration) print('%s leaving the bcs at %s' % (name, env.now)) if __name__ == '__main__': env = Environment() bcs = Resource(env, capacity=2) for i in range(4): env.process(car(env, 'Car {num}'.format(num=str(i)), bcs, i * 2, 5)) env.run()
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 = Container(self.env, capacity=station_capacity, init=station_capacity) self.auto_probe('reservoir', log={}) self.threshold_pct = config.get('gas_station.threshold_pct', 10) 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.env.timeout(10) if self.reservoir_pct < self.threshold_pct: 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))
# 每次服务队列迭代server queue 获取在一次 yield req print(name, 'uer obj is--->', res.users[0], 'at', env.now) # capacity个request都延迟1秒才能释放资源, # with内的yield timeout 用于阻塞正在服务的request一段时间才释放, # 释放后被阻塞的waiting request 立即获得服务资源 yield env.timeout(5) print(name, 'out with', env.now) # with外的yield timeout不会阻塞waiting request获得服务资源, # yield env.timeout(2) print(name, 'out process at',env.now) def multi_process(env, res): num = 0 while True: # yield env.timeout(1) env.process(user(env, res, num)) num += 1 if num >= 10: break if __name__ == '__main__': env = Environment() res = Resource(env, capacity=5) # env.process(multi_process(env, res)) multi_process(env, res) env.run(until=15)
class Block: """ Provides simulation-specific behavior of a basic block. Entities are accepted via the process_entity entry-point, and standard processing steps are executed. Actual processing behavior is to be provided by subclasses, implementing the actual_processing method. """ def __init__(self, env: Environment, name: str, block_capacity=float("inf")): self.name = name self.env: Environment = env self.overall_count_in = 0 on_enter_or_exit_method_callable = Callable[ [Entity, Optional[Block], Optional[Block]], None ] self.do_on_enter_list: List[on_enter_or_exit_method_callable] = [] self.do_on_exit_list: List[on_enter_or_exit_method_callable] = [] self.entities: List[Entity] = [] self.successors: List[Block] = [] self.block_resource = Resource(env=env, capacity=block_capacity) self.state_manager = StateManager(self.on_block_state_change) self.env.process(self.late_state_evaluation()) def on_enter(self, entity: Entity): """called when process_entity starts""" for method in self.do_on_enter_list: method(entity, None, self) def on_exit(self: "Block", entity: Entity, successor: "Block"): """called when an entities leaves the block""" self.entities.remove(entity) for method in self.do_on_exit_list: method(entity, self, successor) self.on_block_change() self.on_entity_movement(entity, successor) def process_entity(self, entity: Entity): """main entry point for entities coming from predecessors""" entity.time_of_last_arrival = self.env.now self.on_enter(entity) self.overall_count_in += 1 yield self.env.process(self._process_entity(entity)) def _process_entity(self, entity: Entity): """internal entry point for start of entity processing without calling on_enter or setting entity.time_of_last_arrival. useful e.g. for wip init""" self.entities.append(entity) self.on_block_change() # processing self.state_manager.increment_state_count(States.busy) entity.current_process = self.env.process(self.actual_processing(entity)) yield entity.current_process # might be used for interrupt # wait for successor successor = self.find_successor(entity) req = successor.block_resource.request() self.state_manager.increment_state_count(States.blocked) self.state_manager.decrement_state_count(States.busy) yield req # wait until the chosen successor is ready # leaving self.block_resource.release(entity.block_resource_request) entity.block_resource_request = req # remember to be released self.on_exit(entity, successor) self.state_manager.decrement_state_count(States.blocked) self.env.process(successor.process_entity(entity)) def find_successor(self, entity: Entity): """find next block to send entity to""" return self.successors[0] def on_block_change(self): """called on block change""" def on_block_state_change(self, state, new_value): """called when state of the block changes""" def on_entity_movement(self, entity: Entity, successor: "Block"): """called on entity movement from this block to the successor""" def late_state_evaluation(self): """schedule evaluation on sim start, when the visualizer has been loaded""" yield self.env.timeout(0) self.state_manager.evaluate_state_count() def actual_processing(self, entity: Entity): """to be implemented by concrete subclasses""" raise NotImplementedError()
def _resource_hospital(self, packages): """""" people_res = Resource(self.env, self.hospital_capacity) with people_res.request() as req: yield req self.env.process(self._put_packages_into_out_queue(packages))
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 = Container(self.env, capacity=station_capacity, init=station_capacity) self.auto_probe('reservoir', log={}) self.threshold_pct = config.get('gas_station.threshold_pct', 10) 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.env.timeout(10) if self.reservoir_pct < self.threshold_pct: 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))