def _evaluate_cancellation_callback(self, event: Event): """Callback detailing how the dispatcher evaluates canceling an order""" self._evaluate_cancellation_event() event.succeed() event.callbacks = []
def _master_process(self, env): min_scheduling_interval = self.min_scheduling_interval scheduling_time = self.scheduling_time timeout = self.env.timeout # We are here intentionally separate registering workers # and task submit, as it is usually separated in real-word # reactors. self.new_workers += self.workers schedule = self.send_update() assert not schedule self.new_tasks += list(self.task_graph.tasks.values()) self.new_objects += list(self.task_graph.objects.values()) schedule = self.send_update() if scheduling_time: yield timeout(scheduling_time) if schedule: self.apply_schedule(schedule) while self.unprocessed_tasks > 0: self.wakeup_event = Event(env) if min_scheduling_interval: yield self.wakeup_event & timeout(min_scheduling_interval) else: yield self.wakeup_event schedule = self.send_update() if scheduling_time: yield timeout(scheduling_time) if schedule: self.apply_schedule(schedule)
def handle(self, from_agent: AgentId, event: WorldEvent) -> Event: """ Main method which governs how events cause each other in the environment. Not to be overridden in children: `handleAction` and `handleWorldEvent` should be overridden instead. """ if isinstance(event, MasterEvent): from_agent = event.agent event = event.inner if isinstance(event, Message): return self.handleMessage(from_agent, event) elif isinstance(event, Action): return self.handleAction(from_agent, event) elif isinstance(event, DelayedEvent): proc = self.env.process(self._delayedHandleGen(from_agent, event)) self.delayed_evs[from_agent][event.id] = proc return Event(self.env).succeed() elif isinstance(event, DelayInterrupt): try: self.delayed_evs[from_agent][event.delay_id].interrupt() except (KeyError, RuntimeError): pass return Event(self.env).succeed() elif from_agent[0] == 'world': return handleWorldEvent(event) else: raise Exception('Non-world event: ' + str(event))
def init(self, env, workers): super().init(env, workers) self.downloads = {} self.recompute_event = Event(env) self.flows = np.zeros((len(workers), len(workers))) self.recompute_flows = False self.flow_cache = LruCache(self.CACHE_SIZE) def network_process(): while True: if self.recompute_flows: self.recompute_flows = False self._recompute_flows() logger.info("Flows reconfigured:\n%s", self.flows) timeout = self._update_speeds() logger.info("Earliest download finished in: %s", timeout) if timeout is not None: start_time = self.env.now r = yield self.recompute_event | self.env.timeout(timeout) self._update_sizes(self.env.now - start_time) if self.recompute_event in r: self.recompute_event = Event(env) else: logger.info("No active downloads") yield self.recompute_event self.recompute_event = Event(env) env.process(network_process())
def shift_controller(self): # Reference to SimPy environment env = self.procflow_sim.simpy_env while True: # Wait for shift on time yield env.timeout(self.shift_on_time) # End the shift (interrupt the process) self.logger.info( "{}\t\t\t{}\t\t(ShiftController)\t\t\t{}\t\t(Process):\t\t\t" "Shift end triggered".format(self.env.now, '<no name>', self.name)) self.shift_end_event.succeed(value={'event': "Shift end"}) self.shift_end_event = Event(env) # Wait for shift off time yield env.timeout(self.shift_off_time) # Start the shift (interrupt the process) self.logger.info( "{}\t\t\t{}\t\t(ShiftController)\t\t\t{}\t\t(Process):\t\t\t" "Shift start triggered".format(self.env.now, '<no name>', self.name)) self.shift_start_event.succeed(value={'event': "Shift start"}) self.shift_start_event = Event(env)
def on_activate(self): # otp_attributes = {'maxWalkDistance': self.env.config.get('drt.default_max_walk'), # 'numItineraries': 1} if self._activity_time_screwed(): yield Event(self.env).succeed() self.env.process(self.unactivatable()) return self.person.update_otp_params() try: direct_trip = self.person.serviceProvider.standalone_osrm_request( self.person) timeout = self.person.get_planning_time(direct_trip) self.person.set_direct_trip(direct_trip) # transit_trip = self.person.serviceProvider.standalone_otp_request(self.person, OtpMode.TRANSIT, # otp_attributes) # timeout = self.person.get_planning_time(transit_trip[0]) if timeout > 0: self.person.update_travel_log(TravellerEventType.ACT_STARTED, self.person.curr_activity) yield self.person.env.timeout(timeout) self.env.process(self.plan()) except (OTPNoPath, OTPTrivialPath) as e: log.warning('{}: {}\n{}'.format(self.env.now, e.msg, e.context)) log.warning( '{}: Person {} will be excluded from the simulation'.format( self.env.now, self.person)) yield Event(self.env).succeed() self.env.process(self.unactivatable())
def _buffer_order_callback(self, event: Event): """Callback detailing how the dispatcher buffers an order once it's placed""" self._buffer_order_event() self._schedule_evaluate_cancellation_event() event.succeed() event.callbacks = []
def _master_process(self, env): self.schedule(self.task_graph.source_nodes(), []) while self.unprocessed_tasks > 0: self.wakeup_event = Event(env) yield self.wakeup_event self.schedule(self.new_ready, self.new_finished) self.new_finished = [] self.new_ready = []
def process(kwargs: SystemArgs): world = kwargs['WORLD'] env = kwargs['ENV'] kitchen_layout = world.component_for_entity(1, KitchenLayout) for _ in range(kitchen_layout.personnel): env.process(cook(kwargs)) yield Event(env)
def _download(self, output, priority): logger.info("Worker %s: scheduled downloading %s, priority=%s", self, output, priority) assert output not in self.data download = Download(Event(self.env), output, priority) self.scheduled_downloads[output] = download return download
def on_plan(self): yield Event(self.env).succeed() while self.env.peek() == self.env.now: # TODO: this makes sure that a request-replan sequence for a person is not broken # if it is, we must save multiple requests and have some policy to merge them yield self.person.env.timeout(0.000001) self.person.update_travel_log(TravellerEventType.ACT_FINISHED, self.person.curr_activity) if self.person.planned_trip is None: try: self.person.update_travel_log( TravellerEventType.TRIP_REQUEST_SUBMITTED, self.person.curr_activity) alternatives = self.person.serviceProvider.request(self.person) self.person.alternatives = alternatives self.person.update_travel_log( TravellerEventType.TRIP_ALTERNATIVES_RECEIVED, self.person.curr_activity) self.env.process(self.choose()) except (OTPTrivialPath, OTPUnreachable) as e: log.warning('{}'.format(e.msg)) log.warning('{}: Excluding person from simulation. {}'.format( self.env.now, self.person)) self.env.process(self.unplannable())
def step(self, action: object) -> Tuple[object, float, bool, dict]: """ takes action and executes simulation until next action is needed """ # 1. preparation self.reward_holder.reset_step_reward() # provide action via event self.action_holder.provided_action.succeed(action) self.action_holder.action_needed = Event(self.model.env) # 2. run model self.model.env.run(until=self.action_holder.action_needed) self.finished_gym_steps_in_current_episode += 1 # 3. collect information to return to agent done = self.check_if_model_is_done() # enable time-dependent rewards time_needed_for_step = self.model.env.now - self.time_of_last_step reward = self.get_reward(time_needed_for_step, done) self.time_of_last_step = self.model.env.now observation = self._get_observation() info = self.get_info() if done: self.finished_episodes += 1 self.finished_gym_steps_overall += 1 return (observation, reward, done, info)
def on_choose(self): """Chooses one of the alternatives according to config.person.mode_choice """ yield Event(self.env).succeed() chosen_trip = self.person.mode_choice.choose(self.person.alternatives) if chosen_trip is None: log.warning( '{}: Trip could not be selected for Person {}.' 'It is possibly because there is no PT and person has no driving license.\n' 'Person will be excluded from simulation.'.format( self.env.now, self.person.id)) log.debug('{}\n{}'.format(self.person, self.person.alternatives)) self.env.process(self.unchoosable()) else: log.info('{}: Person {} have chosen trip {}'.format( self.env.now, self.person.id, chosen_trip)) self.person.planned_trip = chosen_trip.deepcopy() self.person.init_actual_trip() self.person.serviceProvider.start_trip(self.person) self.person.update_travel_log(TravellerEventType.TRIP_CHOSEN, chosen_trip.deepcopy()) # TODO: after choosing, a traveler should wait for beginning of a trip # But that would break the current routing as start tim may be updated by other requests self.env.process(self.execute_trip())
def on_unreactivatable(self): yield Event(self.env).succeed() log.warning( '{}: {} going from {} to {} cannot reach the destination. Ignoring the person.' .format(self.env.now, self.person, self.person.curr_activity.coord, self.person.next_activity.coord)) self.person.serviceProvider.log_unactivatable(self.person) self.person.update_travel_log(TravellerEventType.NO_RUTE)
def on_unchoosable(self): yield Event(self.env).succeed() log.warning( '{}: {} going from {} to {} received none alternatives. Ignoring the person.' .format(self.env.now, self.person, self.person.curr_activity.coord, self.person.next_activity.coord)) self.person.serviceProvider.log_unchoosable(self.person) self.person.update_travel_log(TravellerEventType.NO_RUTE)
def handleAction(self, from_agent: AgentId, action: Action) -> Event: if isinstance(action, PkgRouteAction): to_agent = action.to if not self.conn_graph.has_edge(from_agent, to_agent): raise Exception("Trying to route to a non-neighbor") self.env.process(self._edgeTransfer(from_agent, to_agent, action.pkg)) return Event(self.env).succeed() elif isinstance(action, PkgReceiveAction): logger.debug(f"Package #{action.pkg.id} received at node {from_agent[1]} at time {self.env.now}") self.data_series.logEvent(self.env.now, self.env.now - action.pkg.start_time) return Event(self.env).succeed() else: return super().handleAction(from_agent, action)
def _schedule_log_off_event(self): """Method that allows the courier to schedule the log off time""" log_off_event = Event(env=self.env) log_off_event.callbacks.append(self._log_off_callback) log_off_delay = time_diff(self.off_time, self.on_time) self.env.schedule(event=log_off_event, priority=NORMAL, delay=log_off_delay)
def _schedule_buffer_order_event(self, order: Order): """Method that allows the dispatcher to schedule the order buffering event""" buffering_event = Event(env=self.env) buffering_event.callbacks.append(self._buffer_order_callback) self.env.schedule(event=buffering_event, priority=NORMAL, delay=time_diff(order.preparation_time, order.placement_time))
def _schedule_evaluate_cancellation_event(self): """Method that allows the dispatcher to schedule the cancellation evaluation event""" evaluate_cancellation_event = Event(env=self.env) evaluate_cancellation_event.callbacks.append( self._evaluate_cancellation_callback) self.env.schedule(event=evaluate_cancellation_event, priority=NORMAL, delay=settings.DISPATCHER_WAIT_TO_CANCEL)
def run(self, env, simulator, netmodel): self.env = env self.simulator = simulator self.netmodel = netmodel self.ready_store = Store(env) self.download_wakeup = Event(self.simulator.env) self.free_cpus = self.cpus env.process(self._download_process()) prepared_assignments = [] events = [self.ready_store.get()] while True: finished = yield env.any_of(events) for event in finished.keys(): if event == events[0]: events[0] = self.ready_store.get() assignment = event.value if assignment.cancelled: continue prepared_assignments.append(assignment) prepared_assignments.sort(key=lambda a: a.priority, reverse=True) continue assignment = event.value task = assignment.task self.free_cpus += task.cpus assert not assignment.cancelled del self.assignments[assignment.task] events.remove(event) del self.running_tasks[task] simulator.add_trace_event( TaskEndTraceEvent(self.env.now, self, task)) for output in task.outputs: self._add_data(output) simulator.on_task_finished(self, task) block = float("-inf") for assignment in prepared_assignments[:]: if assignment.priority < block: continue task = assignment.task if task.cpus <= self.free_cpus: prepared_assignments.remove(assignment) if assignment.cancelled: continue self.free_cpus -= task.cpus self.running_tasks[task] = RunningTask(task, self.env.now) simulator.add_trace_event( TaskStartTraceEvent(self.env.now, self, task)) events.append(env.timeout(task.duration, assignment)) self.simulator.on_task_start(self, assignment.task) else: block = max(block, assignment.block)
def network_process(): while True: if self.recompute_flows: self.recompute_flows = False self._recompute_flows() logger.info("Flows reconfigured:\n%s", self.flows) timeout = self._update_speeds() logger.info("Earliest download finished in: %s", timeout) if timeout is not None: start_time = self.env.now r = yield self.recompute_event | self.env.timeout(timeout) self._update_sizes(self.env.now - start_time) if self.recompute_event in r: self.recompute_event = Event(env) else: logger.info("No active downloads") yield self.recompute_event self.recompute_event = Event(env)
def _download_process(self): events = [self.download_wakeup] env = self.env runtime_state = self.simulator.runtime_state while True: finished = yield env.any_of(events) for event in finished.keys(): if event == events[0]: self.download_wakeup = Event(self.simulator.env) events[0] = self.download_wakeup downloads = None continue events.remove(event) download = event.value assert download.output not in self.data self.data.add(download.output) self.running_downloads.remove(download) del self.scheduled_downloads[download.output] download.event.succeed(download) self.simulator.add_trace_event( FetchEndTraceEvent(self.env.now, self, download.source, download.output)) if len(self.running_downloads) < self.max_downloads: # We need to sort any time, as it priority may changed in background if downloads is None: downloads = list( o for o in self.scheduled_downloads.values() if o not in self.running_downloads) downloads.sort(key=lambda d: d.priority, reverse=True) for d in downloads[:]: count = 0 worker = runtime_state.output_info(d.output).placing[0] for rd in self.running_downloads: if worker == rd.source: count += 1 if count >= self.max_downloads_per_worker: continue downloads.remove(d) d.start_time = self.env.now d.source = worker self.running_downloads.append(d) event = self.netmodel.download(worker, self, d.output.size, d) events.append(event) self.simulator.add_trace_event( FetchStartTraceEvent(self.env.now, self, worker, d.output)) if len(self.running_downloads) >= self.max_downloads: break
def reset(self) -> object: """ resets env and simulation model and return initial observation """ self.before_reset() self._initialize_model() # run until the first action is needed self.action_holder.action_needed = Event(self.model.env) self.model.env.run(until=self.action_holder.action_needed) initial_observation = self._get_observation() return initial_observation
def handleMessage(self, from_agent: AgentId, msg: Message) -> Event: """ Method which handles how messages should be dealt with. Is not meant to be overridden. """ if isinstance(msg, WireOutMsg): # Out message is considered to be handled as soon as its # handling by the recipient is scheduled. We do not # wait for other agent to handle them. self.env.process(self._handleOutMsgGen(from_agent, msg)) return Event(self.env).succeed() else: raise UnsupportedMessageType(msg)
def __init__(self, unsched_id, proc, params): self.name = params['name'] self.id = unsched_id self.duration_mean = params['duration_mean'] self.duration_stddev = params['duration_stddev'] self.mtbf = params['mtbf'] # References to simulation and events self.process = proc self.logger = proc.procflow_sim.logger self.procflow_sim = proc.procflow_sim self.shift_start_event = proc.shift_start_event self.env = proc.procflow_sim.simpy_env # Remaining scheduled maintenance duration # (in case the scheduled maintenance activity gets preempted by end of the shift) self.remaining_duration = self.sample_duration() self.remaining_interarrival_time = self.sample_frequency() # The maintenance event to yield on self.maintenance_event = Event(self.env) self.maintenance_complete_event = Event(self.env)
def _master_process(self, env): timeout = self.env.timeout min_scheduling_interval = self.min_scheduling_interval scheduling_time = self.scheduling_time schedule = self.schedule(self.task_graph.source_tasks(), []) if scheduling_time: yield timeout(scheduling_time) if schedule: self.apply_schedule(schedule) while self.unprocessed_tasks > 0: self.wakeup_event = Event(env) if min_scheduling_interval: yield self.wakeup_event & timeout(min_scheduling_interval) else: yield self.wakeup_event schedule = self.schedule(self.new_ready, self.new_finished) self.new_finished = [] self.new_ready = [] if scheduling_time: yield timeout(scheduling_time) if schedule: self.apply_schedule(schedule)
def _checkInterrupt(self, callback): if self.conveyors_move_proc is None: callback() else: try: self.conveyors_move_proc.interrupt() self.conveyors_move_proc = None except RuntimeError as err: self.log('UNEXPECTED INTERRUPT FAIL {}'.format(err), True) for model in self.conveyor_models.values(): model.pause() callback() self._updateAll() return Event(self.env).succeed()
def download(self, source, target, size, value=None): assert source != target event = Event(self.env) rd = RunningDownload(size, event, value) logger.info("New download %s; %s-%s size=%s", rd, source, target, size) key = (source, target) lst = self.downloads.get(key) if lst is None: lst = [] self.downloads[key] = lst if not lst: logger.info("Link %s-%s opened, need recompute flows", source, target) self.recompute_flows = True lst.append(rd) if not self.recompute_event.triggered: self.recompute_event.succeed() return event
def handleAction(self, from_agent: AgentId, action: Action) -> Event: if isinstance(action, BagReceiveAction): assert agent_type( from_agent) == 'sink', "Only sink can receive bags!" bag = action.bag self.log(f"bag #{bag.id} received at sink {from_agent[1]}") if from_agent != bag.dst: raise Exception( f'Bag #{action.bag.id} came to {from_agent}, but its destination was {bag.dst}' ) assert bag.id in self.current_bags, "why leave twice??" self.current_bags.pop(action.bag.id) # fix to make reinforce work from ..agents.routers.reinforce import PackageHistory PackageHistory.finishHistory(bag) self.data_series.logEvent('time', self.env.now, self.env.now - action.bag.start_time) return Event(self.env).succeed() elif isinstance(action, DiverterKickAction): assert agent_type( from_agent) == 'diverter', "Only diverter can do kick actions!" self.log(f'diverter {agent_idx(from_agent)} kicks') return self._checkInterrupt(lambda: self._diverterKick(from_agent)) elif isinstance(action, ConveyorSpeedChangeAction): assert agent_type( from_agent) == 'conveyor', "Only conveyor can change speed!" self.log( f'change conv {agent_idx(from_agent)} speed to {action.new_speed}' ) return self._checkInterrupt(lambda: self._changeConvSpeed( agent_idx(from_agent), action.new_speed)) else: return super().handleAction(from_agent, action)
def _move(self): try: events = all_next_events(self.conveyor_models) self.log('MOVING: {}'.format(events)) if len(events) > 0: conv_idx, (bag, node, delay) = events[0] assert delay > 0, "next event delay is 0!" self.log('NEXT EVENT: conv {} - ({}, {}, {})'.format( conv_idx, bag, node, delay)) yield self.env.timeout(delay) else: # hang forever (until interrupt) yield Event(self.env) for model in self.conveyor_models.values(): model.pause() self._updateAll() except Interrupt: pass