Skip to content

Commit

Permalink
Simulator submit
Browse files Browse the repository at this point in the history
TODO: Test for bugs
TODO: Decide which tests to run
  • Loading branch information
joshtch committed Dec 18, 2016
1 parent 864418f commit 900edeb
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 109 deletions.
67 changes: 48 additions & 19 deletions cloud.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,25 @@
import Queue
if __name__ == "__main__": import packet

# these delays can be seen as the time response time of the server for each game
# being played

HI_P_CLOUD_DELAY = 10
MED_P_CLOUD_DELAY = 5
LOW_P_CLOUD_DELAY = 3
TRAFFIC_DELAY = { 'high' : 10, 'med' : 5, 'low' : 3 }

class Cloud(object):

def __init__(self, gameTraffic, location, timeout, num_players):

self.requestList = Queue.Queue()

if gameTraffic == 0:
self.timeToProcess = LOW_P_CLOUD_DELAY
elif gameTraffic == 1:
self.timeToProcess = MED_P_CLOUD_DELAY
else:
self.timeToProcess = HIGH_P_CLOUD_DELAY
self.timeToProcess = TRAFFIC_DELAY(gameTraffic)

self.num_players = num_players
self.location = location
self.timeMS = 0
self.timeout = timeout

def updateTime(self, time):
self.timeMS = time
return self.processResponse(time)

def processResponse(self, time):
if self.timeMS % self.timeToProcess == 0 and not self.requestList.empty() :
def responseAt(self, time):
if time % self.timeToProcess == 0 and not self.requestList.empty() :
headPacket = self.requestList.get();
if (time - headPacket.timestamp) > self.timeout:
return None
Expand All @@ -40,13 +29,53 @@ def processResponse(self, time):
newPacket = headPacket

# update packet
newPacket.sendAddress = 0
newPacket.receiveAddress = i
newPacket.sender = 0
newPacket.receiver = i

responsePackets.append(newPacket)
return responsePackets
else:
return None

def receiveRequest(self, packet):
def receivePacket(self, packet):
self.requestList.put(packet)


if __name__ == "__main__":
packet1 = packet.Packet(100, 0, 0, 1)
packet2 = packet.Packet( 0, 0, 0, 2)
packet3 = packet.Packet(100, 0, 0, 1)
packet4 = packet.Packet( 100, 0, 0, 2)

cloud = cloud.Cloud(0, 0, 10, 10)

cloud.receivePacket(packet1)
cloud.receivePacket(packet4)
cloud.receivePacket(packet3)
cloud.receivePacket(packet2)

time = 100
print "\ntest 1: should return 3 packets"
for i in range(0,20):
#print time
packetList = cloud.updateTime(time)
if packetList != None:
print packetList[0].packet_id
time = time + 1

cloud.receivePacket(packet1)
cloud.receivePacket(packet2)
cloud.receivePacket(packet3)
cloud.receivePacket(packet4)

time = 100
print "\ntest 2: should return 2 packets"

for i in range(0,20):
#print time
packetList = cloud.updateTime(time)
if packetList != None:
print packetList[0].packet_id
time = time + 1
print "\n"

41 changes: 41 additions & 0 deletions data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import statistics
class Data(object):

def __init__(self):
self.d = {}

def putSend(self, id, sendTime):
if id not in self.d:
self.d[id] = { 'sendTime' : sendTime, 'receiveTime' : -1 }
else:
self.d[id]['sendTime'] = sendTime

def putReceive(self, id, receiveTime):
if id not in self.d:
self.d[id] = { 'sendTime' : -1, 'receiveTime' : receiveTime }
else:
self.d[id]['receiveTime'] = receiveTime

def rawData(self):
return self.d

def sendTime(self, id):
return self.d[id]['sendTime']

def receiveTime(self, id):
return self.d[id]['receiveTime']

def latency(self, id):
if self.receiveTime(id) != -1:
return self.d[id]['receiveTime'] - self.d[id]['sendTime']
else:
return None

def latencyList(self):
return [self.latency(x) for x in self.d if self.latency(x) is not None]

def averageLatency(self):
return statistics.median(self.latencyList)

def numDropped(self):
return len([self.receiveTime(x) for x in self.d if self.receiveTime(x) is None])
44 changes: 13 additions & 31 deletions device.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from game import Game
from packet import Packet
from random import randint
from data import Data

TICKS_PER_EVENT = 5

Expand All @@ -10,42 +11,23 @@ def __init__(self, deviceID, fps, ticksPerEvent, location):
self.deviceID = deviceID
self.fps = fps
self.ticksPerEvent = ticksPerEvent
self.firstEventOffset = randint(0, ticksPerEvent - 1)
self.location = location
self.data = Data()

self.position = (randint(0, 10), randint(5, 20))
self.timeMS = 0
self.packetDict = {}

def updateTime(self, time, packet=None):
if packet is None:
return sendPacket(time)
else:
receivePacket(time, packet)


def sendPacket(self, time):
if time % self.ticksPerEvent == 0:
movePlayer(time)
packet = Packet(time, self.position, 0, self.deviceID)
def responseAt(self, time):
if (time + self.firstEventOffset) % self.ticksPerEvent == 0:
packet = Packet(time, 0, self.deviceID)
id = packet.packet_id
packetDict[id] = {'sendTime' : time, 'receiveTime' : -1}
print 'Device %s. Packet sent: %s ' % (self.deviceID, packet.packet_id)
return packet
return None

def receivePacket(time, packet):
print 'Device %s. Packet received: %s ' % (self.deviceID, packet.packet_id)
id = packet.packet_id
if id in packetDict.keys():
packetDict[id]['receiveTime'] = time
self.data.putSend(id, time)
return [packet]
else:
# stray packet received
packetDict[id] = {'sendTime' : -1, 'receiveTime' : time}

def movePlayer(time):
self.position = ( randint(0, 10), randint(5, 50))

return []

def receivePacket(packet):
id = packet.packet_id
arrival = packet.arriveTime()
self.data.putReceive(id, arrival)


class OculusRift(Device):
Expand Down
4 changes: 2 additions & 2 deletions location.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def getPropagation(self, c1, c2):
return None

locations = {
"Atlanta"
"Atlanta",
"Austin",
"Cambridge",
"Chicago",
Expand Down Expand Up @@ -111,7 +111,7 @@ def createPropagationMatrix(self):
self.addPropagation("San Francsico", "Seattle", 60)
self.addPropagation("San Francsico", "Washington", 41)

self.addPropagation("Washington", "Seattle", 68)
self.addPropagation("Seattle", "Washington", 68)

if __name__ == "__main__":
loc1 = Location("Washington")
Expand Down
16 changes: 8 additions & 8 deletions network.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ def __init__(self, packet_loss_prob):
self.packet_loss_prob = packet_loss_prob / 100.0
pass

def sendPacket(self, packet, loc1, loc2):
def networkDelay(self, loc1, loc2):
# Check for packet loss
if random.random() < self.packet_loss_prob:
return 0
return -1

# Otherwise, return propagation delay
return loc1.propagationDelayFrom(loc2)
Expand All @@ -23,13 +23,13 @@ class TCP(Network):
def __init__(self, packet_loss_prob):
super(TuftsSecure, self).__init__(packet_loss_prob)

def sendPacket(self, packet, loc1, loc2):
propDelay = Network.sendPacket(packet, loc1, loc2)
def networkDelay(self, loc1, loc2):
propDelay = Network.networkDelay(loc1, loc2)
propDelay += TCP.HEADER_PROCESSING
if propDelay >= TCP.HEADER_PROCESSING:
return propDelay * 2
else:
return TCP.TIMEOUT_RATE + self.sendPacket(packet, loc1, loc2)
return TCP.TIMEOUT_RATE + self.networkDelay(loc1, loc2)

class UDP(Network):

Expand All @@ -38,8 +38,8 @@ class UDP(Network):
def __init__(self, packet_loss_prob):
super(TuftsSecure, self).__init__(packet_loss_prob)

def sendPacket(self, packet, loc1, loc2):
propDelay = Network.sendPacket(packet, loc1, loc2)
def networkDelay(self, loc1, loc2):
propDelay = Network.networkDelay(loc1, loc2)
propDelay += UDP.HEADER_PROCESSING
if propDelay >= UDP.HEADER_PROCESSING:
return propDelay
Expand All @@ -54,6 +54,6 @@ def sendPacket(self, packet, loc1, loc2):
# Timeout test
network = Network(2) # 2 percent chance to drop packet
attempts = 0
while network.sendPacket(0, loc1, loc2):
while network.networkDelay(loc1, loc2) != -1:
attempts += 1
print "Network dropped packet after " + str(attempts + 1) + " packets sent"
21 changes: 16 additions & 5 deletions packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@

class Packet(object):

def __init__(self, timestamp, position, receiverAddress, senderAddress):
def __init__(self, timestamp, receiver, sender):
self._init_packet_id()
self.timestamp = timestamp
self.position = position
self.receiverAddress = receiverAddress
self.senderAddress = senderAddress
self.receiver = receiver
self.sender = sender
self.elapsedTime = 0
self.phase = 'network->cloud'

def _init_packet_id(self):
self.packet_id = uuid.uuid4() # generate a random UUID
self.packet_id = uuid.uuid4() # generate a random UUID

def addLatency(self, elapsedTime):
self.elapsedTime += elapsedTime

def isReady(self, time):
return timestamp + elapsedTime >= time

def arriveTime(self):
return timestamp + elapsedTime

91 changes: 83 additions & 8 deletions simulator.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,88 @@
import cloud, device, game, network
import cloud, device, network, packet, data
import heapq

CLOUD = 0
MAX_PACKETS_PER_STEP = 350 # https://blog.cloudflare.com/how-to-receive-a-million-packets/

class Simulator(object):

def __init__ (cloud, network, device, game):
self.cloud = cloud
def __init__(self, cloud, network, devices, simTime):
self.endpoints = [cloud] + devices
self.network = network
self.device = device
self.game = game
self.time = 0
self.endTime = simTime

# Priority queue of tuples (timestamp+elapsedtime, packet)
self.activePackets = []

def run(self):
for timeStep in xrange(self.endTime):
self.runStep(timeStep)

def getResults(self):
results = []
for deviceID in xrange(1, self.numEndpoints()):
results.append(self.endpoints[deviceID].data)
return results

def numEndpoints(self):
return len(self.endpoints)

def runStep(self, step):
self.deliverReadyPackets(step)
self.updateActivePackets(step)

def sendThruNetwork(self, packet):
senderLocation = self.locationOf(packet.sender)
receiverLocation = self.locationOf(packet.receiver)
response = self.network.networkDelay(senderLocation, receiverLocation)
if notDropped(reponse):
delay = response
packet.addLatency(delay)
return packet
else:
return None

def addActivePacket(self, packet):
toAdd = (packet.arriveTime(), packet)
heapq.heappush(self.activePackets, toAdd)

def nextActivePacket(self):
return self.activePackets[0][1]

def hasActivePackets(self):
return len(self.activePackets) > 0

def popActivePacket(self):
return heapq.heappop(self.activePackets)

def deliverReadyPackets(self, step):
self.resetDeliveryCounts()
if self.hasActivePackets() and self.nextActivePacket().isReady(step):
self.deliverPacket(self.popActivePacket())

def deliverPacket(self, packet):
dest = packet.receiver
if self.deliveryCounts[dest] <= MAX_PACKETS_PER_STEP:
self.endpoints[dest].receivePacket(packetToDeliver)
self.incrementDeliveryCountFor(dest)

def incrementDeliveryCountFor(self, deviceID):
self.deliveryCounts[deviceID] += 1

def updateActivePackets(self, step):
for endpoint in self.endpoints:
response = endpoint.responseAt(step)
for packet in response:
self.queuePacket(packet)

def queuePacket(packet):
networkResponse = self.sendThruNetwork(packet)
if isinstance(networkResponse, Packet):
self.addActivePacket(networkResponse)

def resetDeliveryCounts(self):
self.deliveryCounts = [0] * len(self.endpoints)

def run():
# TODO: Use the 4 classes to produce meaningful data
pass
def locationOf(self, deviceID):
return self.endpoints[deviceID].location
Loading

0 comments on commit 900edeb

Please sign in to comment.