예제 #1
0
파일: qsim.py 프로젝트: larioandr/pyqumo
class QueueingSystem(Model):
    """QueueingSystem model represents a simple queue */*/1/N or */*/1,
    where any distribution can be used for arrival and service times.

    This model consists of four children:
    - `queue`: a model representing the packets queue (`Queue`)
    - `source`: a model representing the packets source (`Source`)
    - `server`: a model representing the packets server (`Server`)
    - `sink`: a model which collects served packets (`Sink`)
    """
    def __init__(self, sim):
        super().__init__(sim)

        arrival = sim.params.arrival
        service = sim.params.service
        queue_capacity = sim.params.queue_capacity

        self.children['queue'] = Queue(sim, queue_capacity)
        self.children['source'] = Source(sim, arrival, index=0)
        self.children['server'] = Server(sim, service)
        self.children['sink'] = Sink(sim)

        # Building connections:
        self.source.connections['queue'] = self.queue
        self.queue.connections['server'] = self.server
        self.server.connections['queue'] = self.queue
        self.server.connections['next'] = self.sink

        # Statistics:
        self.system_size_trace = Trace()
        self.system_size_trace.record(self.sim.stime, 0)
        self.system_wait_intervals = Statistic()

    @property
    def queue(self):
        return self.children['queue']

    @property
    def source(self):
        return self.children['source']

    @property
    def server(self):
        return self.children['server']

    @property
    def sink(self):
        return self.children['sink']

    @property
    def system_size(self):
        return self.queue.size + (1 if self.server.busy else 0)

    def update_system_size(self, index=0):
        assert index == 0
        self.system_size_trace.record(self.sim.stime, self.system_size)

    def add_system_wait_interval(self, value, index=0):
        assert index == 0
        self.system_wait_intervals.append(value)
예제 #2
0
    def __init__(
        self,
        sim,
        address=None,
        sifs=None,
        phy_header_size=None,
        ack_size=None,
    ):
        super().__init__(sim)

        # Properties:
        self.__address = address
        self.__sifs = sifs if sifs is not None else sim.params.sifs
        self.__phy_header_size = (phy_header_size if phy_header_size
                                  is not None else sim.params.phy_header_size)
        self.__ack_size = (ack_size
                           if ack_size is not None else sim.params.ack_size)

        # State variables:
        self.__state = Receiver.State.IDLE
        self.__rxbuf = set()
        self.__cur_tx_pdu = None

        # Statistics:
        self.__num_collisions = 0
        self.__num_received = 0
        self.__busy_trace = Trace()
        self.__busy_trace.record(sim.stime, 0)
예제 #3
0
 def __init__(self, sim, capacity):
     super().__init__(sim)
     self.__capacity = capacity
     self.__size = 0
     # Statistics:
     self.size_trace = Trace()
     self.size_trace.record(self.sim.stime, 0)
예제 #4
0
 def __init__(self, sim, service_mean):
     super().__init__(sim)
     self.__service_mean = service_mean
     self.__busy = False
     # Statistics:
     self.delays = Statistic()
     self.busy_trace = Trace()
     self.busy_trace.record(self.sim.stime, 0)
예제 #5
0
def test_getting_pmf(data, probs):
    trace = Trace(data)
    pmf = trace.pmf()
    assert set(pmf.keys()) == set(probs.keys())

    estimated, expected = [], []
    for v, p in probs.items():
        estimated.append(pmf[v])
        expected.append(p)
    np.testing.assert_almost_equal(estimated, expected)
예제 #6
0
def test_getting_timeavg(data):
    def estimate():
        interval = data[-1][0] - data[0][0]
        acc = 0
        for i in range(1, len(data)):
            acc += data[i - 1][1] * (data[i][0] - data[i - 1][0]) / interval
        return acc

    trace = Trace(data)
    estimated_avg = estimate()
    np.testing.assert_almost_equal(trace.timeavg(), estimated_avg)
예제 #7
0
파일: qsim.py 프로젝트: larioandr/pyqumo
    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
예제 #8
0
파일: qsim.py 프로젝트: larioandr/pyqumo
 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()
예제 #9
0
        def __init__(self, arrival_mean, service_mean):
            # Parameters:
            self.arrival_mean = arrival_mean
            self.service_mean = service_mean

            # System state:
            self.queue_size = 0
            self.server_busy = False

            # Statistics:
            self.system_size_trace = Trace()
            self.server_busy_trace = Trace()
            self.service_intervals = Statistic()
            self.arrivals = Intervals()
            self.departures = Intervals()
예제 #10
0
class QueueingSystem(Model):
    """QueueingSystem model is the top-level model for this test.

    This model consists of four children:
    - `queue`: a model representing the packets queue (`Queue`)
    - `source`: a model representing the packets source (`Source`)
    - `server`: a model representing the packets server (`Server`)
    - `sink`: a model which collects served packets (`Sink`)
    """
    def __init__(self, sim):
        super().__init__(sim)
        self.children['queue'] = Queue(sim, sim.params.capacity)
        self.children['source'] = Source(sim, sim.params.arrival_mean)
        self.children['server'] = Server(sim, sim.params.service_mean)
        self.children['sink'] = Sink(sim)

        # Building connections:
        self.source.connections['queue'] = self.queue
        self.queue.connections['server'] = self.server
        self.server.connections['queue'] = self.queue
        self.server.connections['sink'] = self.sink

        # Statistics:
        self.system_size_trace = Trace()
        self.system_size_trace.record(self.sim.stime, 0)
    
    @property
    def queue(self):
        return self.children['queue']
    
    @property
    def source(self):
        return self.children['source']
    
    @property
    def server(self):
        return self.children['server']
    
    @property
    def sink(self):
        return self.children['sink']
    
    @property
    def system_size(self):
        return self.queue.size + (1 if self.server.busy else 0)
    
    def update_system_size(self):
        self.system_size_trace.record(self.sim.stime, self.system_size)
예제 #11
0
def test_record_adds_data():
    trace = Trace()
    assert trace.as_tuple() == ()

    trace.record(t=1, v=10)
    assert trace.as_tuple() == ((1, 10), )

    trace.record(t=2, v=13)
    assert trace.as_tuple() == (
        (1, 10),
        (2, 13),
    )
예제 #12
0
    def __init__(self, sim):
        super().__init__(sim)
        self.children['queue'] = Queue(sim, sim.params.capacity)
        self.children['source'] = Source(sim, sim.params.arrival_mean)
        self.children['server'] = Server(sim, sim.params.service_mean)
        self.children['sink'] = Sink(sim)

        # Building connections:
        self.source.connections['queue'] = self.queue
        self.queue.connections['server'] = self.server
        self.server.connections['queue'] = self.queue
        self.server.connections['sink'] = self.sink

        # Statistics:
        self.system_size_trace = Trace()
        self.system_size_trace.record(self.sim.stime, 0)
예제 #13
0
    def __init__(
        self,
        sim,
        address=None,
        phy_header_size=None,
        mac_header_size=None,
        ack_size=None,
        bitrate=None,
        preamble=None,
        max_propagation=0,
    ):
        super().__init__(sim)

        # Properties:
        self.__address = address
        self.__phy_header_size = (phy_header_size if phy_header_size
                                  is not None else sim.params.phy_header_size)
        self.__mac_header_size = (mac_header_size if mac_header_size
                                  is not None else sim.params.mac_header_size)
        self.__bitrate = bitrate if bitrate is not None else sim.params.bitrate
        self.__preamble = (preamble
                           if preamble is not None else sim.params.preamble)
        self.__max_propagation = max_propagation
        self.__ack_size = (ack_size
                           if ack_size is not None else sim.params.ack_size)

        # State variables:
        self.timeout = None
        self.cw = 65536
        self.backoff = -1
        self.num_retries = None
        self.pdu = None
        self.__state = Transmitter.State.IDLE
        self.__seqn = 0

        # Statistics:
        self.backoff_vector = Statistic()
        self.__start_service_time = None
        self.service_time = Statistic()
        self.num_sent = 0
        self.num_retries_vector = Statistic()
        self.__busy_trace = Trace()
        self.__busy_trace.record(sim.stime, 0)

        # Initialize:
        sim.schedule(0, self.start)
예제 #14
0
파일: qsim.py 프로젝트: larioandr/pyqumo
    def __init__(self, sim):
        super().__init__(sim)

        arrival = sim.params.arrival
        service = sim.params.service
        queue_capacity = sim.params.queue_capacity

        self.children['queue'] = Queue(sim, queue_capacity)
        self.children['source'] = Source(sim, arrival, index=0)
        self.children['server'] = Server(sim, service)
        self.children['sink'] = Sink(sim)

        # Building connections:
        self.source.connections['queue'] = self.queue
        self.queue.connections['server'] = self.server
        self.server.connections['queue'] = self.queue
        self.server.connections['next'] = self.sink

        # Statistics:
        self.system_size_trace = Trace()
        self.system_size_trace.record(self.sim.stime, 0)
        self.system_wait_intervals = Statistic()
예제 #15
0
    def __init__(self, sim):
        super().__init__(sim)

        # Topology comprises:
        # - `nodes`: each having position, properties
        # - `static_connections`: optional dictionary with static connections
        # We use it as a template for network creation.
        self.network = Network(sim.params.topology)
        self.repair_started = False
        self.failed_nodes = []
        self.routing_mode = sim.params.routing_mode
        sim.logger.level = sim.params.log_level

        # Statistics:
        self.num_failed = Trace()
        self.num_offline = Trace()
        self.operable = Trace()
        self.num_failed_sampled = Trace()
        self.num_offline_sampled = Trace()

        # Record initial trace data:
        t = sim.stime
        num_offline = len(self.network.get_offline_nodes())
        self.num_failed.record(t, 0)
        self.num_failed_sampled.record(t, 0)
        self.num_offline.record(t, num_offline)
        self.num_offline_sampled.record(t, num_offline)
        self.operable.record(t, 1)

        # Build network routes:
        self.network.build_routing_table(self.routing_mode)
        for sensor in self.network.sensors():
            self.schedule_failure(sensor)

        # Check whether we record samples:
        if sim.params.record_samples:
            sim.schedule(sim.params.sample_interval,
                         self.handle_sample_timeout)
예제 #16
0
 def __init__(self, sim, bitrate=inf, header_size=0, preamble=0, ifs=0):
     super().__init__(sim)
     self.bitrate = bitrate
     self.header_size = header_size
     self.preamble = preamble
     self.ifs = ifs
     # State variables:
     self.__started = False
     self.__tx_frame = None
     self.__wait_ifs = False
     self.__rx_frame = None
     # Statistics:
     self.__num_received_frames = 0
     self.__num_received_bits = 0
     self.__rx_busy_trace = Trace()
     self.__rx_busy_trace.record(0, 0)
     self.__num_transmitted_packets = 0
     self.__num_transmitted_bits = 0
     self.__tx_busy_trace = Trace()
     self.__tx_busy_trace.record(0, 0)
     self.__service_time = Statistic()
     self.__service_started_at = None
     # Initialization:
     self.sim.schedule(self.sim.stime, self.start)
예제 #17
0
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

    Parent: `QueueingSystem`
    """
    def __init__(self, sim, service_mean):
        super().__init__(sim)
        self.__service_mean = service_mean
        self.__busy = False
        # Statistics:
        self.delays = Statistic()
        self.busy_trace = Trace()
        self.busy_trace.record(self.sim.stime, 0)
    
    @property
    def service_mean(self):
        return self.__service_mean
    
    @property
    def busy(self):
        return self.__busy
    
    def on_service_end(self):
        assert self.__busy
        queue = self.connections['queue'].module
        self.__busy = False
        self.busy_trace.record(self.sim.stime, 0)
        if queue.size > 0:
            queue.pop()
            self.start_service()
        self.connections['sink'].module.receive_packet()
        self.parent.update_system_size()

    def start_service(self):
        assert not self.__busy
        delay = exponential(self.service_mean)
        self.sim.schedule(delay, self.on_service_end)
        self.delays.append(delay)
        self.__busy = True
        self.busy_trace.record(self.sim.stime, 1)
예제 #18
0
파일: qsim.py 프로젝트: larioandr/pyqumo
    def __init__(self, sim):
        super().__init__(sim)

        arrivals = sim.params.arrivals
        services = sim.params.services
        queue_capacity = sim.params.queue_capacity
        n = sim.params.num_stations
        active_sources = {i for i, ar in enumerate(arrivals) if ar is not None}

        if n < 1:
            raise ValueError('num_stations must be >= 1')

        self.queues, self.sources, self.servers = [], [], []
        for i in range(n):
            self.queues.append(Queue(sim, queue_capacity, i))
            self.servers.append(Server(sim, services[i], i))
            if i in active_sources:
                self.sources.append(Source(sim, arrivals[i], i))
            else:
                self.sources.append(None)
        self.sink = Sink(sim)
        self.children['queue'] = self.queues
        self.children['server'] = self.servers
        self.children['sink'] = self.sink
        self.children['sources'] = [src for src in self.sources if src]

        # Connecting modules:
        for i in range(n):
            self.queues[i].connections['server'] = self.servers[i]
            self.servers[i].connections['queue'] = self.queues[i]
            if self.sources[i]:
                self.sources[i].connections['queue'] = self.queues[i]
            if i < n - 1:
                self.servers[i].connections['next'] = self.queues[i + 1]
            else:
                self.servers[i].connections['next'] = self.sink

        # Statistics:
        self.system_size_trace = [Trace() for _ in range(n)]
        for i in range(n):
            self.system_size_trace[i].record(sim.stime, 0)
        self.system_wait_intervals = [Statistic() for _ in range(n)]
예제 #19
0
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
    """
    def __init__(self, sim, capacity):
        super().__init__(sim)
        self.__capacity = capacity
        self.__size = 0
        # Statistics:
        self.size_trace = Trace()
        self.size_trace.record(self.sim.stime, 0)
    
    @property
    def capacity(self):
        return self.__capacity
    
    @property
    def size(self):
        return self.__size
    
    def push(self):
        server = self.connections['server'].module
        if self.__size == 0 and not server.busy:
            server.start_service()
        elif self.capacity < 0 or self.__size < self.capacity:
            self.__size += 1
            self.size_trace.record(self.sim.stime, self.__size)
        self.parent.update_system_size()
    
    def pop(self):
        assert self.__size > 0
        self.__size -= 1
        self.size_trace.record(self.sim.stime, self.__size)
    
    def __str__(self):
        return f'Queue({self.size})'
예제 #20
0
class ModelData(Model):
    def __init__(self, sim):
        super().__init__(sim)

        # Topology comprises:
        # - `nodes`: each having position, properties
        # - `static_connections`: optional dictionary with static connections
        # We use it as a template for network creation.
        self.network = Network(sim.params.topology)
        self.repair_started = False
        self.failed_nodes = []
        self.routing_mode = sim.params.routing_mode
        sim.logger.level = sim.params.log_level

        # Statistics:
        self.num_failed = Trace()
        self.num_offline = Trace()
        self.operable = Trace()
        self.num_failed_sampled = Trace()
        self.num_offline_sampled = Trace()

        # Record initial trace data:
        t = sim.stime
        num_offline = len(self.network.get_offline_nodes())
        self.num_failed.record(t, 0)
        self.num_failed_sampled.record(t, 0)
        self.num_offline.record(t, num_offline)
        self.num_offline_sampled.record(t, num_offline)
        self.operable.record(t, 1)

        # Build network routes:
        self.network.build_routing_table(self.routing_mode)
        for sensor in self.network.sensors():
            self.schedule_failure(sensor)

        # Check whether we record samples:
        if sim.params.record_samples:
            sim.schedule(sim.params.sample_interval,
                         self.handle_sample_timeout)

    def handle_failure(self, node):
        """Handle node failure. Upon failure the following actions take place:

        1) a node is marked as turned off in the network;

        2) if repair hasn't started yet, we check whether enough nodes
           already failed:
           if num_offline_nodes > num_offline_till_repair, start the repair.
        """
        self.network.turn_off(node)
        self.failed_nodes.append(node)

        # If static routing is used, no actual re-routing will take place.
        # However, in case of dynamic routing using, this can lead to
        # network reconfiguration and new alternative paths
        self.network.build_routing_table(self.routing_mode)

        # Count failed and offline nodes and record statistics:
        num_offline_nodes = len(self.network.get_offline_nodes())
        num_failed_nodes = len(self.failed_nodes)
        self.num_failed.record(self.sim.stime, num_failed_nodes)
        self.num_offline.record(self.sim.stime, num_offline_nodes)

        # We treat all offline nodes as broken, while some of them are
        # offline since the routers they were connected to became unavailable:
        self.sim.logger.debug(f'node {node} failed. {num_failed_nodes} nodes '
                              f'failed, {num_failed_nodes} are offline. '
                              f'Failed nodes: {self.failed_nodes}')
        if num_offline_nodes >= self.sim.params.num_offline_till_repair:
            if not self.repair_started:
                # print(f'num failed node: {num_failed_nodes}, offline: {num_offline_nodes}, start repair')
                self.schedule_next_repair()
            self.operable.record(self.sim.stime, 0)

    def handle_repair_finished(self, node):
        """Handle repair of all nodes finished.

        Here we assume that during repair all failed nodes were fixed,
        so we schedule their failures again.
        """
        # Turn on the repaired node, rebuild a routing table and schedule the
        # next failure:
        self.sim.logger.debug(f'node {node} repair finished')
        self.network.turn_on(node)
        self.network.build_routing_table(self.routing_mode)
        self.schedule_failure(node)

        # Remove the repaired node from the failed list, count failed and
        # offline nodes:
        self.failed_nodes.remove(node)
        num_failed = len(self.failed_nodes)
        num_offline = len(self.network.get_offline_nodes())

        # Record statistics:
        t = self.sim.stime
        self.num_failed.record(t, num_failed)
        self.num_offline.record(t, num_offline)

        # Check whether there are other failed nodes. If they exist, schedule
        # the next repair. Otherwise, mark repair end.
        if num_failed > 0:
            self.schedule_next_repair()
        else:
            self.repair_started = False

        if num_offline >= self.sim.params.num_offline_till_repair:
            self.operable.record(t, 0)
        else:
            self.operable.record(t, 1)

    def handle_sample_timeout(self):
        t = self.sim.stime
        num_offline = len(self.network.get_offline_nodes())
        self.num_failed_sampled.record(t, len(self.failed_nodes))
        self.num_offline_sampled.record(t, num_offline)
        self.sim.schedule(self.sim.params.sample_interval,
                          self.handle_sample_timeout)

    def schedule_failure(self, node):
        """Schedule next failure event for a given node.

        :param node: network node
        """
        interval = self.sim.params.failure_interval()
        self.sim.schedule(interval, self.handle_failure, args=(node, ))

    def schedule_next_repair(self):
        """Schedule next repair duration. Since all broken nodes are
        repaired at once, we do not distinguish nodes here.
        """
        if not self.failed_nodes:
            raise RuntimeError('can not schedule repair - no failed nodes')

        # Select a random node to repair, and schedule repair end:
        node_index = np.random.randint(len(self.failed_nodes))
        node = self.failed_nodes[node_index]

        if not self.repair_started:
            interval = self.sim.params.repair_interval()
            self.repair_started = True
        else:
            interval = self.sim.params.cons_repair_interval()

        self.sim.logger.debug(f'started node {node} repair for {interval}s')
        self.sim.schedule(interval, self.handle_repair_finished, args=(node, ))
예제 #21
0
def test_trace_creation_with_unordered_timestamps_raises_error(data):
    with pytest.raises(ValueError) as excinfo:
        Trace(data)
    assert 'data must be ordered by time' in str(excinfo.value).lower()
예제 #22
0
def test_trace_creation_with_wrong_mode_raises_error():
    with pytest.raises(ValueError) as excinfo:
        Trace([(1, ), (2, )], mode='wrong')
    assert 'invalid mode' in str(excinfo.value).lower()
예제 #23
0
def test_trace_creation_with_2x2_data_without_mode_is_the_same_as_samples():
    data = ((1, 2), (10, 20))  # t=1, v=10; t=2, v=20
    trace_default = Trace(data)
    trace_samples = Trace(data, mode='samples')
    assert trace_default.as_tuple() == trace_samples.as_tuple()
예제 #24
0
def test_trace_creation_with_2x2_data_using_mode_argument(mode, expected):
    trace = Trace(((1, 2), (10, 20)), mode=mode)
    assert trace.as_tuple() == expected
예제 #25
0
def test_trace_creation_raises_error_when_passed_data_with_wrong_shape(data):
    with pytest.raises(ValueError) as excinfo:
        Trace(data)
    assert 'wrong data shape' in str(excinfo.value).lower()
예제 #26
0
def test_trace_creation_with_single_item(data):
    trace = Trace(data)
    assert not trace.empty
    assert len(trace) == 1
    assert trace.as_tuple() == ((1, 10), )
예제 #27
0
def test_trace_creation_with_valid_data_given_in_pairs():
    data = [(1, 5), (2, 8), (3, 40), (9, 34)]
    trace = Trace(data)
    assert not trace.empty
    assert len(trace) == 4
    assert trace.as_tuple() == tuple(data)
예제 #28
0
def test_asarray_with_illegal_mode_raises_error():
    trace = Trace([[0, 0], [1, 5]])
    with pytest.raises(ValueError) as excinfo:
        trace.asarray('wrong mode')
    assert 'invalid mode' in str(excinfo.value).lower()
예제 #29
0
def test_asarray(data, mode, expected):
    trace = Trace(data)
    actual = trace.asarray(mode)
    assert isinstance(actual, np.ndarray)
    np.testing.assert_almost_equal(actual, expected)
예제 #30
0
def test_as_tuple(data, mode, expected):
    trace = Trace(data)
    if expected is None:
        expected = data
    assert trace.as_tuple(mode=mode) == expected