Skip to content

Commit

Permalink
Merge pull request #11 from phets/master
Browse files Browse the repository at this point in the history
Added comments, renamed internal variables to bits
  • Loading branch information
dsolmann authored Apr 3, 2019
2 parents 1a313f5 + b3e9210 commit 5d76a60
Showing 1 changed file with 99 additions and 25 deletions.
124 changes: 99 additions & 25 deletions Telemachus/src/UpLinkDownLinkRate.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,62 @@
//Author: Richard Bunt
//Author: Richard Bunt
using System;
using System.Collections.Generic;
using System.Text;

namespace Telemachus
{
/// <summary>
/// Class to keep track of the Uplink and Downlink data rates.
/// </summary>
public class UpLinkDownLinkRate
{
#region Constants

/// <summary>
/// Number of data points to be held in the <see cref="upLinkRate"/> and <see cref="downLinkRate"/> lists.
/// </summary>
public const int DEFAULT_AVERAGE_SIZE = 20;

#endregion

#region Fields

// Why exactly five and not some other interval?
private static TimeSpan TIME_SPAN_5_SECONDS = new TimeSpan(0, 0, 5);
private static DateTime TIME_ARBITRARY = System.DateTime.Now;

/// <summary>
/// The number of data points to be held in the <see cref="upLinkRate"/> and <see cref="downLinkRate"/> lists
/// over which <see cref="average"/> performs its average.
/// </summary>
private int averageSize = DEFAULT_AVERAGE_SIZE;

/// <summary>
/// The list that will hold how much data we recieved from the client in bits
/// </summary>
private LinkedList<KeyValuePair<DateTime, int>> upLinkRate = new LinkedList<KeyValuePair<DateTime, int>>();
/// <summary>
/// The list that will hold how much data we sent to the client in bits
/// </summary>
private LinkedList<KeyValuePair<DateTime, int>> downLinkRate = new LinkedList<KeyValuePair<DateTime, int>>();

#endregion

#region Constructors

/// <summary>
/// <para>Empty constructor.</para>
/// <para><see cref="averageSize"/> == <see cref="DEFAULT_AVERAGE_SIZE"/></para>
/// </summary>
public UpLinkDownLinkRate()
{

}

/// <summary>
/// Parametrized constructor sets <see cref="averageSize"/> to a custom value.
/// </summary>
/// <param name="averageSize">The desired number of data points in the LinkedLists.</param>
public UpLinkDownLinkRate(int averageSize)
{
this.averageSize = averageSize;
Expand All @@ -41,25 +66,37 @@ public UpLinkDownLinkRate(int averageSize)

#region Accessors

/// <summary>Add a data point for bytes recieved from the client (UPlink)</summary>
/// <summary>
/// Adds a data point for bits recieved from the client (UPlink). Performs internal bytes*8 conversion.
/// </summary>
public void RecieveDataFromClient(int bytes)
{
// Convert to bits for the data rate
// Convert to bits for the data rate.
addGuardedPoint(DateTime.Now, bytes*8, upLinkRate);
}

/// <summary>Add a data point for bytes sent to the client (DOWNlink)</summary>
/// <summary>
/// Adds a data point for bits sent to the client (DOWNlink). Performs internal bytes*8 conversion.
/// </summary>
public void SendDataToClient(int bytes)
{
// Convert to bits for the data rate
// Convert to bits for the data rate.
addGuardedPoint(DateTime.Now, bytes*8, downLinkRate);
}

/// <summary>
/// Gets the average Downlink rate over the last 5 seconds computed by <see cref="average"/>
/// </summary>
/// <returns>double: Average Downlink rate over the last 5 seconds in bits/s</returns>
public double getDownLinkRate()
{
return average(downLinkRate);
}

/// <summary>
/// Gets the average Uplink rate over the last 5 seconds computed by <see cref="average"/>
/// </summary>
/// <returns>double: Average Uplink rate over the last 5 seconds in bits/s</returns>
public double getUpLinkRate()
{
return average(upLinkRate);
Expand All @@ -69,72 +106,109 @@ public double getUpLinkRate()

#region Private Methods

private void addGuardedPoint(DateTime time, int bytes, LinkedList<KeyValuePair<DateTime, int>> rate)
/// <summary>
/// Adds a data point to the list passed in as <paramref name="rate"/> argument.
/// <para>Keeps the number of data points in the <paramref name="rate"/> list at or below the number of points specified in <see cref="averageSize"/>.</para>
/// </summary>
/// <param name="time">The DateTime for the point to add, will normally be DateTime.Now.</param>
/// <param name="bits">The number of bits for the point to add.</param>
/// <param name="rate">The LinkedList where the data point is added.</param>
private void addGuardedPoint(DateTime time, int bits, LinkedList<KeyValuePair<DateTime, int>> rate)
{
// Locks the LinkedList from being accessed simulatneously by another method.
lock (rate)
{
rate.AddFirst(new KeyValuePair<DateTime, int>(time, bytes));

// Adds a new item to the top of the list.
rate.AddFirst(new KeyValuePair<DateTime, int>(time, bits));

// Checks if the list has grown beyond averageSize.
if (rate.Count >= averageSize)
{
// Removes the last (oldest) element from the list.
rate.RemoveLast();
}
}
}

/// <summary>
/// Computes the average data rate over the last 5 seconds or over the entire rate list, whichever is strictest.
/// </summary>
/// <param name="rate">The LinkedList of data points to average.</param>
/// <returns>The datarate in bits/s as a double.</returns>
private double average(LinkedList<KeyValuePair<DateTime, int>> rate)
{
// Locks the LinkedList from being accessed simulatneously by another method.
lock (rate)
{
if (rate.Count > 1)
// Returns if we don't have at least two data points to average over a time interval.
if(rate.Count < 2)
{
return 0;
}
// Otherwise proceeds with the averaging.
else
{
DateTime newestTime = TIME_ARBITRARY, lastTime = TIME_ARBITRARY, thresholdTime = System.DateTime.Now.Subtract(TIME_SPAN_5_SECONDS);
long totalBytes = 0;
// Two DateTimes that will hold the boundaries of our average.
DateTime newestTime = TIME_ARBITRARY;
DateTime lastTime = TIME_ARBITRARY;

// A time 5 seconds ago. Method will only average over the last 5 seconds.
DateTime thresholdTime = System.DateTime.Now.Subtract(TIME_SPAN_5_SECONDS);

// Accumulator.
long totalBits = 0;
// Tracks times through the following foreach loop.
int irel = 0;

// Cycles through the list in newest to oldest order.
foreach (KeyValuePair<DateTime, int> point in rate)
{
if (totalBytes == 0)
// Sets the first boundary for the average.
if (totalBits == 0)
{
newestTime = point.Key;
}

totalBytes += point.Value;
// Accumulates the amount of data.
totalBits += point.Value;
// Updates the last boundary.
lastTime = point.Key;

// Breaks if we've gone beyond the desired time span.
if (point.Key < thresholdTime)
{
break;
}

// Updates number of times we've been through the foreach.
irel++;
}

if (irel > 0)
// Returns 0 if we don't have any datapoints.
if(irel <= 0)
{
return 0;
}
// Otherwise proceeds with averaging.
else
{
// Calculates the time interval.
double delta = newestTime.Subtract(lastTime).TotalSeconds;

if (delta == 0)
{
return (double)totalBytes;
// If the interval is zero avoids dividing by zero and returns total bits.
return (double)totalBits;
}
else
{
return ((double)totalBytes) / delta;
// Returns the average data rate.
return ((double)totalBits) / delta;
}
}
else
{
return 0;
}
}
else
{
return 0;
}
}
}

#endregion
}
}

0 comments on commit 5d76a60

Please sign in to comment.