SimComponents包
在这里直接将开源仿真包下载粘贴如下
"""
A bit more detailed set of components to use in packet switching
queueing experiments.
Copyright 2014 Greg M. Bernstein
Released under the MIT license
"""
import simpy
import random
import copy
from simpy.core import BoundClass
from simpy.resources import base
from heapq import heappush, heappop
class Packet(object):
""" A very simple class that represents a packet.
This packet will run through a queue at a switch output port.
We use a float to represent the size of the packet in bytes so that
we can compare to ideal M/M/1 queues.
Parameters
----------
time : float
the time the packet arrives at the output queue.
size : float
the size of the packet in bytes
id : int
an identifier for the packet
src, dst : int
identifiers for source and destination
flow_id : int
small integer that can be used to identify a flow
"""
def __init__(self, time, size, id, src="a", dst="z", flow_id=0,env=1):
self.env = env
self.time = time
self.size = size
self.id = id
self.src = src
self.dst = dst
self.flow_id = flow_id
def __repr__(self):
now=self.env.now
return "id: {0}, src: {1}, startTime: {2}, endTime: {3:.2f}, costTime: {4:.2f}, size: {5:.2f}".\
format(self.id, self.src, self.time, now, now-self.time, self.size)
class PacketGenerator(object):
""" Generates packets with given inter-arrival time distribution.
Set the "out" member variable to the entity to receive the packet.
Parameters
----------
env : simpy.Environment
the simulation environment
adist : function
a no parameter function that returns the successive inter-arrival times of the packets
sdist : function
a no parameter function that returns the successive sizes of the packets
initial_delay : number
Starts generation after an initial delay. Default = 0
finish : number
Stops generation at the finish time. Default is infinite
"""
def __init__(self, env, id, adist, sdist, initial_delay=0, finish=float("inf"), flow_id=0):
self.id = id
self.env = env
self.adist = adist
self.sdist = sdist
self.initial_delay = initial_delay
self.finish = finish
self.out = None
self.packets_sent = 0
self.action = env.process(self.run()) # starts the run() method as a SimPy process
self.flow_id = flow_id
def run(self):
"""The generator function used in simulations.
"""
yield self.env.timeout(self.initial_delay)
while self.env.now < self.finish:
# wait for next transmission
yield self.env.timeout(self.adist())
self.packets_sent += 1
p = Packet(self.env.now, self.sdist, self.packets_sent, src=self.id, flow_id=[i for i in self.flow_id],env=self.env)
self.out.put(p)
class PacketSink(object):
""" Receives packets and collects delay information into the
waits list. You can then use this list to look at delay statistics.
Parameters
----------
env : simpy.Environment
the simulation environment
debug : boolean
if true then the contents of each packet will be printed as it is received.
rec_arrivals : boolean
if true then arrivals will be recorded
absolute_arrivals : boolean
if true absolute arrival times will be recorded, otherwise the time between consecutive arrivals
is recorded.
rec_waits : boolean
if true waiting time experienced by each packet is recorded
selector: a function that takes a packet and returns a boolean
used for selective statistics. Default none.
"""
def __init__(self, env, rec_arrivals=False, absolute_arrivals=False, rec_waits=True, debug=False, selector=None):
self.store = simpy.Store(env)
self.env = env
self.rec_waits = rec_waits
self.rec_arrivals = rec_arrivals
self.absolute_arrivals = absolute_arrivals
self.waits = []
self.arrivals = []
self.debug = debug
self.packets_rec = 0
self.bytes_rec = 0
self.selector = selector
self.last_arrival = 0.0
def put(self, pkt):
if not self.selector or self.selector(pkt):
now = self.env.now
if self.rec_waits:
self.waits.append(self.env.now - pkt.time)
if self.rec_arrivals:
if self.absolute_arrivals:
self.arrivals.append(now)
else:
self.arrivals.append(now - self.last_arrival)
self.last_arrival = now
self.packets_rec += 1
self.bytes_rec += pkt.size
if self.debug:
print(pkt)
class SwitchPort(object):
""" Models a switch output port with a given rate and buffer size limit in bytes.
Set the "out" member variable to the entity to receive the packet.
Parameters
----------
env : simpy.Environment
the simulation environment
rate : float
the bit rate of the port
qlimit : integer (or None)
a buffer size limit in bytes or packets for the queue (including items
in service).
limit_bytes : If true, the queue limit will be based on bytes if false the
queue limit will be based on packets.
"""
def __init__(self, env, rate, qlimit=None, limit_bytes=True, debug=False):
self.store = simpy.Store(env)
self.rate = rate
self.env = env
self.out = None
self.packets_rec = 0
self.packets_drop = 0
self.qlimit = qlimit
self.limit_bytes = limit_bytes
self.byte_size = 0 # Current size of the queue in bytes
self.debug = debug
self.busy = 0 # Used to track if a packet is currently being sent
self.action = env.process(self.run()) # starts the run() method as a SimPy process
def run(self):
while True:
msg = (yield self.store.get())
self.busy = 1
self.byte_size -= msg.size
yield self.env.timeout(msg.size*8.0/self.rate)
self.out.put(msg)
self.busy = 0
if self.debug:
print(msg)
def put(self, pkt):
self.packets_rec += 1
tmp_byte_count = self.byte_size + pkt.size
if self.qlimit is None:
self.byte_size = tmp_byte_count
return self.store.put(pkt)
if self.limit_bytes and tmp_byte_count >= self.qlimit:
self.packets_drop += 1
return
elif not self.limit_bytes and len(self.store.items) >= self.qlimit-1:
self.packets_drop += 1
else:
self.byte_size = tmp_byte_count
return self.store.put(pkt)
class RandomBrancher(object):
""" A demultiplexing element that chooses the output port at random.
Contains a list of output ports of the same length as the probability list
in the constructor. Use these to connect to other network elements.
Parameters
----------
env : simpy.Environment
the simulation environment
probs : List
list of probabilities for the corresponding output ports
"""
def __init__(self, env, probs):
self.env = env
self.probs = probs
self.ranges = [sum(probs[0:n+1]) for n in range(len(probs))] # Partial sums of probs
if self.ranges[-1] - 1.0 > 1.0e-6:
raise Exception("Probabilities must sum to 1.0")
self.n_ports = len(self.probs)
self.outs = [None for i in range(self.n_ports)] # Create and initialize output ports
self.packets_rec = 0
def put(self, pkt):
self.packets_rec += 1
rand = random.random()
for i in range(self.n_ports):
if rand < self.ranges[i]:
if self.outs[i]: # A check to make sure the output has been assigned before we put to it
self.outs[i].put(pkt)
return
class FlowDemux(object):
""" A demultiplexing element that splits packet streams by flow_id.
Contains a list of output ports of the same length as the probability list
in the constructor. Use these to connect to other network elements.
Parameters
----------
outs : List
list of probabilities for the corresponding output ports
"""
def __init__(self, outs=None, default=None):
self.outs = outs
self.default = default
self.packets_rec = 0
def put(self, pkt):
self.packets_rec += 1
'''
flow_id = pkt.flow_id
print(flow_id)
print(self.outs[flow_id])
if flow_id < len(self.outs):
self.outs[flow_id].put(pkt)
else:
if self.default:
self.default.put(pkt)'''
#将flow_id从列表修改为字典
flow_id = pkt.flow_id.pop()
if flow_id in self.outs.keys():
self.outs[flow_id].put(pkt)
else:
if self.default:
self.default.put(pkt)
class SnoopSplitter(object):
""" A snoop port like splitter. Sends the original packet out port 1
and sends a copy of the packet out port 2.
You need to set the values of out1 and out2.
"""
def __init__(self):
self.out1 = None
self.out2 = None
def put(self, pkt):
pkt2 = copy.copy(pkt)
if self.out1:
self.out1.put(pkt)
if self.out2:
self.out2.put(pkt2)
"""
Trying to implement a stamped/ordered version of the Simpy Store class.
The "stamp" is used to sort the elements for removal ordering. This
can be used in the implementation of sophisticated queueing disciplines, but
would be overkill for fixed priority schemes.
Copyright 2014 Greg M. Bernstein
Released under the MIT license
"""