class Disk(Process): mean_service_time = 0.02 # Mean service time for one IO request. def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.state.collect("Idle") @Chain def initialize(self): sim = self.sim queue = sim.members["Queue"] while True: if queue.size.get() == 0: self.state.collect("Idle") yield Poll.greater_than(0, queue.size) self.state.collect("Busy") io_request = queue.get() io_request.service = sim.time yield sample.nonnegative(sim.rng.expovariate, 1.0 / Disk.mean_service_time) io_request.departure = self.sim.time queue.collect(io_request) def finalize(self): queue = self.sim.members["Queue"] if queue.running: queue.stop() # Make sure to finalize the queue before the disk. self.state.collect(self.state.last_value()) self.state.pareto_chart(axes=queue.plotter, title=self.name) print self, "utilization:", self.state.wrel_frequency("Busy")
class WorkStation(Process): """Configuration parameters: initial_workers :: int throughput_fnc :: callable(int) -> float changeover :: {(from, to): float} setup :: {product: float} """ state = TSeries() workers = Checkable(0) throughput = 0.0 current = None initial_workers = 0 changeover = {} setup = {} def set_workers(self, workers): """This method is used by the line balancer to adjust the number of workers in each assembly line and/or the workbenches, and update their throughput.""" self.throughput = self.throughput_fnc(workers) self.workers.set(workers) def throughput_fnc(self, workers): raise NotImplementedError() def status(self): return "%d workers (throughput=%f) - %s - %s" % \ (self.workers.get(), self.throughput, self.state.last_value(), self.current) def reset(self): if "queue" not in self: self["queue"] = Store() self["queue"].content.clear() self.state = TSeries(storing=False, numeric=False, time_fnc=self.sim.clock.get) self.workers = Checkable(self.initial_workers) self.throughput = self.throughput_fnc(self.initial_workers) self.current = None self.previous = None @Chain def initialize(self): queue = self["queue"] while True: yield self.fetch(queue) yield self.prepare(self.current.product) yield self.process(self.current) self.previous = self.current.product self.current.action.succeed() self.current = None @Chain def fetch(self, queue): """Get the order with minimum preparation time and maximum process time from the queue.""" self.state.collect(IDLE) if len(queue.content) == 0: self.current = (yield queue.content.get()).result else: best_score = None best_order = None for order in queue.content: changeover = self.changeover.get((self.previous, order.product), 0.0) setup = self.setup.get(order.product, 0.0) ptime = order.quantity * order.product.complexity[self.name] score = (-(changeover + setup), +ptime) if score > best_score: best_score = score best_order = order queue.content.remove(best_order) self.current = best_order self.current.location = self.name @Chain def prepare(self, product): """Prepare the workstation for processing the specified product. This may include a changeover if the product is different from the one previously processed, and a setup time for preparing the machinery to start working.""" self.state.collect(CHANGEOVER) yield self.changeover.get((self.previous, product), 0.0) self.state.collect(SETUP) yield self.setup.get(product, 0.0) @Chain def process(self, order): """Simulate the processing of 'order' with a possibly variable number of workers.""" self.state.collect(BUSY) remaining = order.quantity while True: if self.workers.get() == 0: self.state.collect(NO_WORKERS) yield Poll.greater_than(0, self.workers) self.state.collect(BUSY) rate = self.throughput / order.product.complexity[self.name] process = Delay(remaining / rate) yield Renege(process, Poll.not_equal_to(self.workers.get(), self.workers)) remaining -= process.elapsed_time * rate if process.succeeded(): raise Chain.success() def finalize(self): self.state.collect(self.state.last_value()) self.state.pareto_chart(axes=self.sim.plotter, title=self.name)