class Source(Model): """Source module represents the traffic source with exponential intervals. Connections: queue Handlers: - handle_timeout(): called upon next arrival timeout Statistics: - intervals: `Intervals`, stores a set of inter-arrival intervals Parent: `QueueingSystem` """ def __init__(self, sim, arrival, index): super().__init__(sim) self.arrival = arrival self.index = index # Statistics: self.intervals = Intervals() self.num_generated = 0 self.delays = Statistic() # Initialize: self._schedule_next_arrival() def handle_timeout(self): packet = Packet(self, self.sim.stime) self.connections['queue'].send(packet) self._schedule_next_arrival() self.num_generated += 1 def _schedule_next_arrival(self): self.intervals.record(self.sim.stime) self.sim.schedule(self.arrival(), self.handle_timeout)
class Source(Model): """Source module represents the traffic source with exponential intervals. Connections: queue Handlers: - on_timeout(): called upon next arrival timeout Statistics: - intervals: `Intervals`, stores a set of inter-arrival intervals Parent: `QueueingSystem` """ def __init__(self, sim, arrival_mean): super().__init__(sim) self.__arrival_mean = arrival_mean # Statistics: self.intervals = Intervals() # Initialize: self._schedule_next_arrival() @property def arrival_mean(self): return self.__arrival_mean def on_timeout(self): queue = self.connections['queue'].module queue.push() self._schedule_next_arrival() def _schedule_next_arrival(self): self.intervals.record(self.sim.stime) self.sim.schedule(exponential(self.arrival_mean), self.on_timeout)
def test_record_valid_data(): ints = Intervals() assert ints.as_tuple() == () ints.record(2) assert_almost_equal(ints.as_tuple(), (2, )) ints.record(3.4) assert_almost_equal(ints.as_tuple(), (2, 1.4))
class Sink(Model): """Sink module represents the traffic sink and counts arrived packets. Methods: - receive_packet(): called when the server finishes serving packet. """ def __init__(self, sim): super().__init__(sim) # Statistics: self.departures = Intervals() self.departures.record(self.sim.stime) def receive_packet(self): self.departures.record(self.sim.stime)
class Sink(Model): """Sink module represents the traffic sink and counts arrived packets. Methods: - receive_packet(): called when the server finishes serving packet. """ def __init__(self, sim): super().__init__(sim) # Statistics: self.arrival_intervals = Intervals() self.arrival_intervals.record(self.sim.stime) def handle_message(self, message, connection=None, sender=None): assert isinstance(message, Packet) self.arrival_intervals.record(self.sim.stime) message.source.delays.append(self.sim.stime - message.created_at)
def test_record_illegal_types_raises_error(value): ints = Intervals() with pytest.raises(TypeError) as excinfo: ints.record(value) assert 'only numeric values expected' in str(excinfo.value).lower()
def test_record_in_past_raises_error(): ints = Intervals([10]) with pytest.raises(ValueError) as excinfo: ints.record(9.9) assert 'prohibited timestamps from past' in str(excinfo.value).lower()
def test_intervals_copy_list_content_instead_of_pointer(): data = [1, 2] ints = Intervals(data) ints.record(3) assert ints.as_tuple() == (1, 1, 1) assert data == [1, 2]
class Server(Model): """Server module represents a packet server with exponential service time. Connections: queue, sink Handlers: - on_service_end(): called upon service timeout Methods: - start_service(): start new packet service; generate error if busy. Statistics: - delays: `Statistic`, stores a set of service intervals - busy_trace: `Trace`, stores a vector of server busy status - num_served: `int` Parent: `QueueingSystem` """ def __init__(self, sim, service_time, index=0): super().__init__(sim) self.service_time = service_time self.packet = None self.index = index # Statistics: self.service_intervals = Statistic() self.busy_trace = Trace() self.busy_trace.record(self.sim.stime, 0) self.departure_intervals = Intervals() self.departure_intervals.record(sim.stime) self.num_served = 0 @property def busy(self): return self.packet is not None @property def ready(self): return self.packet is None def handle_service_end(self): assert self.busy stime = self.sim.stime self.connections['next'].send(self.packet) self.parent.add_system_wait_interval(stime - self.packet.arrived_at, self.index) self.packet = None self.busy_trace.record(stime, 0) self.departure_intervals.record(stime) self.num_served += 1 # Requesting next packet from the queue: queue = self.connections['queue'].module if queue.size > 0: self.serve(queue.pop()) self.parent.update_system_size(self.index) def _start_service(self): delay = self.service_time() self.sim.schedule(delay, self.handle_service_end) return delay def serve(self, packet): assert not self.busy self.packet = packet delay = self._start_service() self.service_intervals.append(delay) self.busy_trace.record(self.sim.stime, 1)
class Queue(Model): """Queue module represents the packets queue, stores only current size. Connections: server Methods and properties: - push(): increase the queue size - pop(): decrease the queue size - size: get current queue size Statistics: - size_trace: Trace, holding the history of the queue size updates - num_arrived: `int` - num_dropped: `int` - drop_ratio: `float` """ def __init__(self, sim, capacity, index=0): super().__init__(sim) self.__capacity = capacity self.packets = deque() self.index = index # Statistics: self.size_trace = Trace() self.size_trace.record(self.sim.stime, 0) self.num_dropped = 0 self.arrival_intervals = Intervals() self.arrival_intervals.record(sim.stime) self.num_arrived = 0 self.wait_intervals = Statistic() @property def capacity(self): return self.__capacity @property def size(self): return len(self.packets) @property def drop_ratio(self): if self.num_arrived == 0: return 0 return self.num_dropped / self.num_arrived def handle_message(self, message, connection=None, sender=None): self.push(message) def push(self, packet): self.arrival_intervals.record(self.sim.stime) self.num_arrived += 1 server = self.connections['server'].module packet.arrived_at = self.sim.stime if self.size == 0 and not server.busy: server.serve(packet) self.wait_intervals.append(self.sim.stime - packet.arrived_at) elif self.capacity is None or self.size < self.capacity: self.packets.append(packet) self.size_trace.record(self.sim.stime, self.size) else: self.num_dropped += 1 self.parent.update_system_size(self.index) def pop(self): packet = self.packets.popleft() self.size_trace.record(self.sim.stime, self.size) self.wait_intervals.append(self.sim.stime - packet.arrived_at) return packet def __str__(self): return f'Queue({self.size})'