Network system simulation
Summary
The purpose of this lab is to simulate the operation of the output link of a router that is
modeled as a queuing system and investigate its performance under variable configurations,
to understand how different parameter settings may affect the system behaviour and how
modifying some parameters of the network system may impact on performance metrics.
In the considered queuing system, representing the output link of a router, the customers
represent the packets that arrive to that output link, whereas the service represents the
transmission on the channel. Finally, the waiting line represents the buffer where packets
are stored before transmission.
Tasks
In order to investigate the performance of the router output link, you should focus on the
following aspects:
a. Derive the metrics describing the system performance in the baseline scenario, considering both the cases with and without packet losses.
b. Modify the baseline scenario varying the size of the buffer and considering a multiserver system, assuming that two transmitters are present in the system. Test the
system behaviour under different buffer sizes (infinite waiting line, medium to short
finite waiting line, no buffer at all) and compare its performance with the single server
scenario.
You can start from the network simulator code available in the course material, that represents the operation of an M/M/1 queuing system with infinite waiting line, 
using an event scheduling approach and the python code that is based on an event scheduling approach;

You can make your modifications and integration to the code, in order to investigate the
system performance via simulation. Different metrics can be useful to understand the system
operation and evaluate its performance, like:
number of transmitted packets
• number of dropped packets
• average number of packets in the system
• average queuing delay, i.e. the average time spent in the system
• distribution of queuing delay
• average waiting delay per packet, assuming that the packet waiting delay is
the time elapsed from the moment at which a packet enters the system to the
instant at
which the service begins.
It is useful to compute both of the following types of waiting delay:
– the average waiting delay experienced by any packet (averaging over all packets);
– the average waiting delay considering only those packets that actually experience some delay, since they enter the system while the server(s) is (are)
busy and are hence buffered.
• average buffer occupancy, i.e. the average number of packets in the buffer.
• loss probability, i.e. the fraction of packets that cannot be transmitted since the
server(s) is (are) currently busy and no buffer is present in the system or, in case of
finite buffer, it is full.
• busy time, i.e. the cumulative time that during the simulation each server spends in
a busy state serving packets requests. In a single-server system, you can observe how
it varies with the buffer size. In a multi-server system, based on the busy time analysis
per each server, you can examine the distribution of the load among servers.
You can think of other metrics that may be helpful in your analysis to highlight specific
aspects or critical issues in the system operation. When running your simulations, pay
attention on setting appropriate values of the simulation duration.
In your analysis of the network operation and performance, you should perform the following
tasks:
1. investigate the system performance under different arrival rates, keeping a fixed
value for the average service rate:
(a) how is the system affected? Highlight your findings by showing the variation of
relevant performance metrics;
(b) set a desired confidence level and, for one of the metrics reported in the graphs
produced in task 1.a, show the confidence intervals of the performed measurements;
(c) are the results of your analysis consistent with theoretical expected values?2. consider a multi-server system, assuming two transmitters and a finite buffer:
(a) compare its performance with the single server case;
(b) test different values of the buffer size: how do they affect the system performance
with respect to an infinite buffer?
3. considering a multi-server scenario, investigate the load distribution among servers.
Test different algorithms to assign each new request to an available server (random
assignment; round robin; assignment to the fastest servers, assuming for example that
each server features a different service rate...) and compare the system performance in
the various cases. How is load distribution affected?
4. try to vary the distribution of the service time, i.e. considering the case of M/G/1,
and observe how the system performance changes, assuming one or more different
distribution types for the service time instead of exponential distribution;

Hey GPT! I have the following code for performing the lab exercise I provided above(this code is the code mentioned in the text above). Look at the code and tell me how I should use this code for performing simulation.(How and what should be modified for performing each part of the simulation and how to create plots)

#!/usr/bin/python3

import random
from queue import Queue, PriorityQueue

# ******************************************************************************
# Constants
# ******************************************************************************

SERVICE = 10.0 # SERVICE is the average service time; service rate = 1/SERVICE
ARRIVAL = 5.0 # ARRIVAL is the average inter-arrival time; arrival rate = 1/ARRIVAL
LOAD=SERVICE/ARRIVAL # This relationship holds for M/M/1

TYPE1 = 1

SIM_TIME = 500000

arrivals=0
users=0
BusyServer=False # True: server is currently busy; False: server is currently idle

MM1=[]


# ******************************************************************************
# To take the measurements
# ******************************************************************************
class Measure:
def init(self,Narr,Ndep,NAveraegUser,OldTimeEvent,AverageDelay):
self.arr = Narr
self.dep = Ndep
self.ut = NAveraegUser
self.oldT = OldTimeEvent
self.delay = AverageDelay

# ******************************************************************************
# Client
# ******************************************************************************
class Client:
def init(self,type,arrival_time):
self.type = type
self.arrival_time = arrival_time

# ******************************************************************************
# Server
# ******************************************************************************
class Server(object):

# constructor
def init(self):

# whether the server is idle or not
self.idle = True


# ******************************************************************************

# arrivals ********************************************************************
def arrival(time, FES, queue):
global users

#print(“Arrival no. “,data.arr+1,” at time “,time,” with “,users,” users” )

# cumulate statistics
data.arr += 1
data.ut += users(time-data.oldT)
data.oldT = time

# sample the time until the next event
inter_arrival = random.expovariate(lambd=1.0/ARRIVAL)

# schedule the next arrival
FES.put((time + inter_arrival, “arrival”))

users += 1

# create a record for the client
client = Client(TYPE1,time)

# insert the record in the queue
queue.append(client)

# if the server is idle start the service
if users==1:

# sample the service time
service_time = random.expovariate(1.0/SERVICE)
#service_time = 1 + random.uniform(0, SEVICE_TIME)

# schedule when the client will finish the server
FES.put((time + service_time, “departure”))

# ******************************************************************************

# departures ******************************************************************
def departure(time, FES, queue):
global users

#print(“Departure no. “,data.dep+1,” at time “,time,” with “,users,” users” )

# cumulate statistics
data.dep += 1
data.ut += users(time-data.oldT)
data.oldT = time

# get the first element from the queue
client = queue.pop(0)

# do whatever we need to do when clients go away

data.delay += (time-client.arrival_time)
users -= 1

# see whether there are more clients to in the line
if users >0:
# sample the service time
service_time = random.expovariate(1.0/SERVICE)

# schedule when the client will finish the server
FES.put((time + service_time, “departure”))


# ******************************************************************************
# the “main” of the simulation
# ******************************************************************************

random.seed(42)

data = Measure(0,0,0,0,0)

# the simulation time
time = 0

# the list of events in the form: (time, type)
FES = PriorityQueue()


# schedule the first arrival at t=0
FES.put((0, “arrival”))

# simulate until the simulated time reaches a constant
while time < SIM_TIME:
(time, event_type) = FES.get()

if event_type == “arrival”:
arrival(time, FES, MM1)

elif event_type == “departure”:
departure(time, FES, MM1)

# print output data
print(“MEASUREMENTS \n\nNo. of users in the queue:”,users,“\nNo. of arrivals =”,
data.arr,“- No. of departures =”,data.dep)

print("Load: “,SERVICE/ARRIVAL)
print(”\nArrival rate: “,data.arr/time,” - Departure rate: “,data.dep/time)

print(”\nAverage number of users: ",data.ut/time)

print("Average delay: “,data.delay/data.dep)
print(“Actual queue size: “,len(MM1))

if len(MM1)>0:
print(“Arrival time of the last element in the queue:”,MM1[len(MM1)-1].arrival_time)