package Ethernet;
import java.util.UUID;
import java.util.HashMap;
import java.lang.Math;
import java.util.Random;
public class Host {
public static boolean FAIRNESS_MODE = false;
public int hostId;
int repeaterIndex;
Transmitter tstate;
Receiver rstate;
SimulatedTime stime;
SimulatedEvent expectedTransDone;
Random r = new Random();
int transAttempt = 0;
int processTime;
//int packetsToSend = 2;
int packetSize;
int packetsSent = 0;
int packetsDropped = 0;
double totalCollisionSlots = 0;
//double avgCollisionSlotVal = 0;
int numCollisionPeriods = 0;
double totalTransDelay = 0;
//double avgTransDelay = 0;
double packRdyTime = 0;
//HashMap<UUID, SimulatedEvent> upcomingEvents;
// obsolete measure
//int position; // define this to be the time units from the left-most edge
// of the network
//int repeaterIndex;
public static double PREAMBLE_TIME = 64.0;
public static double GAP_TIME = 96.0;
public static double JAMMING_TIME = 32.0;
public static double SLOT_TIME = 512.0;
// number of feet per bit
public static double FEET_PER_BIT = 65.6167979;
// distance to nearest repeater in feet
public static double DISTANCE_TO_REPEATER = 20;
// distance between two hosts excluding the distance of their repeaters
public static double BIT_DISTANCE_BETWEEN_TWO_HOSTS = 2 * DISTANCE_TO_REPEATER / FEET_PER_BIT;
// accounts for the preamble of each packet, and interpacket gap, but we are excluding the CRC since that is part of the header which
// is accounted for in the packet size
public static int OVERHEAD_BITS_PER_PACKET = 20 * 8;
public static int MAX_PROCESS_TIME = 1250;
public static int MIN_PROCESS_TIME = 1000;
public Host(SimulatedTime parentTime, int pSize, int inID)
{
hostId = inID;
// For our fairness test, set up an extremely unfair topology.
if (FAIRNESS_MODE) {
// hosts 0-22 are on repeater 0, host 23 is on repeater 10, 10,000 feet away
repeaterIndex = (inID < 4) ? 0 : 10;
} else {
//Set up the topology normally.
Double grpOfSix = new Double(Math.floor(inID / 6.0));
repeaterIndex = grpOfSix.intValue();
}
tstate = Transmitter.PREPARING;
rstate = Receiver.IDLE;
stime = parentTime;
// packetSize in host is in bits, but it comes in as bytes
packetSize = pSize * 8;
//position = curPos;
processTime = 10;
//upcomingEvents = new HashMap<UUID, SimulatedEvent>();
}
public void StartMeasuring()
{
//avgTransDelay = 0;
totalTransDelay = 0;
//avgCollisionSlotVal = 0;
totalCollisionSlots = 0;
numCollisionPeriods = 0;
packetsSent = 0;
packetsDropped = 0;
}
public double RandomProcessTime()
{
return (double)(MIN_PROCESS_TIME + r.nextInt(MAX_PROCESS_TIME - MIN_PROCESS_TIME));
}
public double BitDistanceFromAnotherRepeaterIndex(int otherRptInd)
{
return Math.abs(repeaterIndex - otherRptInd) * 1000.0 / FEET_PER_BIT;
}
public boolean isMyEvent(SimulatedEvent e){
return e.hostCreated == this.hostId;
}
public void scheduleMyEvent(SimulatedEvent.SimEvtType type, double startoffset, boolean justMyself){
UUID id = UUID.randomUUID();
double currentTime = stime.getCurrentTime();
SimulatedEvent event = new SimulatedEvent(id, type, currentTime + startoffset, startoffset, this.hostId, repeaterIndex, justMyself);
stime.schedule(event);
if(type == SimulatedEvent.SimEvtType.TRANS_DONE) {
expectedTransDone = event;
}
}
public double reportAverageWaitSlots()
{
return totalCollisionSlots / numCollisionPeriods;
}
public int reportTotalCollisionPeriods()
{
return numCollisionPeriods;
}
public int reportTotalPacketsSent()
{
return packetsSent;
}
public int reportTotalPacketsDropped()
{
return packetsDropped;
}
public int reportTotalBitsSent()
{
// including overhead
return (packetSize + OVERHEAD_BITS_PER_PACKET) * packetsSent;
}
public double reportAverageTransDelay()
{
return totalTransDelay / packetsSent;
}
public void reactToEvent(SimulatedEvent e) {
double relpos = BIT_DISTANCE_BETWEEN_TWO_HOSTS + BitDistanceFromAnotherRepeaterIndex(e.RepeaterIndexOfHost);
//Determine the state changes of the receiver
switch (rstate) {
case BUSY:
//Line is busy. If we see the end of a transmission or jamming.
if (e.getEventType() == SimulatedEvent.SimEvtType.JAMMING_DONE ||
e.getEventType() == SimulatedEvent.SimEvtType.TRANS_DONE ){
//If: The END I see came from another host AND I am supposed to see it.
// Reschedule the END so it's my END and only I can see it.
//Else: Take this rescheduled event, now I experience the event, Go to Gap state,
// and schedule for myself when the gap is over
if ( !isMyEvent(e) && !e.justMyself ){
scheduleMyEvent(e.getEventType(), relpos, true);
} else if (e.justMyself && isMyEvent(e)){
rstate = Receiver.GAP;
scheduleMyEvent(SimulatedEvent.SimEvtType.GAP_DONE, GAP_TIME, true);
}
}
break;
case GAP:
//If I see that MY Gap time is over, then schedule a notification to myself that
//My receiver is now idle, and move to the idle state.
if (isMyEvent(e) && e.getEventType() == SimulatedEvent.SimEvtType.GAP_DONE) {
//scheduleMyEvent(SimulatedEvent.SimEvtType.R_NOW_IDLE, 0, true);
rstate = Receiver.IDLE;
}
break;
case IDLE:
//If: The line is idle and we see the start of a signal
if ((e.getEventType() == SimulatedEvent.SimEvtType.JAMMING_START ||
e.getEventType() == SimulatedEvent.SimEvtType.PREAMBLE_START ||
e.getEventType() == SimulatedEvent.SimEvtType.TRANS_START )) {
//If: The START I see came from another host AND I am supposed to see it.
// Reschedule the START so it's my START and only I can see it.
//Else: Take this rescheduled event, now I experience the event, Go to BUSY state,
// and schedule a notification for myself that the receiver has become busy.
if ( !isMyEvent(e) && !e.justMyself ) {
scheduleMyEvent(e.getEventType(),relpos, true);
} else if (e.justMyself && isMyEvent(e)){
//scheduleMyEvent(SimulatedEvent.SimEvtType.R_NOW_BUSY, 0, true);
rstate = Receiver.BUSY;
}
}
// if we see a trans_done and it's my own and it is not a pseudo-event, wait gap
else if(e.getEventType() == SimulatedEvent.SimEvtType.TRANS_DONE && isMyEvent(e) && !e.justMyself)
{
scheduleMyEvent(SimulatedEvent.SimEvtType.GAP_DONE, GAP_TIME, true);
rstate = Receiver.GAP;
}
break;
}
// determine state changes of the transmitter
switch (tstate) {
case EAGER:
//I am eager to send packets, and I see that MY receiver is idle, I start my preamble, and schedule when I end.
//if (e.getEventType() == SimulatedEvent.SimEvtType.R_NOW_IDLE && isMyEvent(e)){
if(rstate == Receiver.IDLE){
//(rstate == Receiver.IDLE) {
if(transAttempt == 0)
{
packRdyTime = stime.getCurrentTime();
}
scheduleMyEvent(SimulatedEvent.SimEvtType.PREAMBLE_START, 0, false);
scheduleMyEvent(SimulatedEvent.SimEvtType.PREAMBLE_DONE, PREAMBLE_TIME, true);
tstate = Transmitter.PREAMBLE;
}
break;
case PREAMBLE:
// I am transmitting my preamble, when MY preamble is done,
// If: Receiver is idle, start packet, schedule packet end, go to sending state
// Else: Collision occurred so start jamming, schedule jamming end, go to jamming state.
if (e.getEventType() == SimulatedEvent.SimEvtType.PREAMBLE_DONE && isMyEvent(e) ) {
if (rstate == Receiver.IDLE) {
scheduleMyEvent(SimulatedEvent.SimEvtType.TRANS_START, 0, false);
scheduleMyEvent(SimulatedEvent.SimEvtType.TRANS_DONE, packetSize, false);
tstate = Transmitter.SENDING;
} else {
scheduleMyEvent(SimulatedEvent.SimEvtType.JAMMING_START, 0, false);
scheduleMyEvent(SimulatedEvent.SimEvtType.JAMMING_DONE, JAMMING_TIME, false);
tstate = Transmitter.JAMMING;
}
}
break;
case JAMMING:
//If my jamming is done. Schedule when my backoff will be over
//Increment the transAttempt. Go to WAITING backoff slots state.
if (e.getEventType() == SimulatedEvent.SimEvtType.JAMMING_DONE && isMyEvent(e)) {
int maxWaitSlots = 1023; // 2 ^ 10 - 1
if(transAttempt < 10)
{
Double power = new Double(Math.pow(2.0, transAttempt));
maxWaitSlots = power.intValue();
}
int numK = r.nextInt(maxWaitSlots);
//int avgCollisionSlotVal = 0;
//int numCollisionPeriods = 0;
// new average is the old average times number of prior periods + current val / number of periods
//avgCollisionSlotVal = (numCollisionPeriods * avgCollisionSlotVal + numK) / (numCollisionPeriods + 1);
// alternatively, just add up the numerator to divide later, duh.
totalCollisionSlots += numK;
numCollisionPeriods++;
scheduleMyEvent(SimulatedEvent.SimEvtType.BACKOFF_DONE,SLOT_TIME *((double)numK), true);
transAttempt++;
tstate = Transmitter.WAITING;
}
break;
case SENDING:
//I am sending packets.
//If my receiver has become busy, start jamming, schedule jamming end, go to jamming state
//Else: If the transmission is complete, If I have no more to send, I'm done
// Else: schedule when the next packet will be ready, go to preparing state
// if (e.getEventType() == SimulatedEvent.SimEvtType.R_NOW_BUSY && isMyEvent(e)) {
if (rstate == Receiver.BUSY){
scheduleMyEvent(SimulatedEvent.SimEvtType.JAMMING_START, 0, false);
scheduleMyEvent(SimulatedEvent.SimEvtType.JAMMING_DONE, JAMMING_TIME, false);
tstate = Transmitter.JAMMING;
stime.deschedule(expectedTransDone);
} else if (e.getEventType() == SimulatedEvent.SimEvtType.TRANS_DONE && isMyEvent(e)) {
// measure delay as current time minus packRdyTime, averaged out over packetsSent
//avgTransDelay = ((avgTransDelay * packetsSent) + (stime.getCurrentTime() - packRdyTime)) / (packetsSent + 1);
// alternatively, sum up transDelay to divide later, duh
totalTransDelay += stime.getCurrentTime() - packRdyTime;
// successfully sent packet, record it, reset k to 0
packetsSent++;
transAttempt = 0;
scheduleMyEvent(SimulatedEvent.SimEvtType.PACKET_READY, RandomProcessTime(), true);
tstate = Transmitter.PREPARING;
}
break;
case PREPARING:
// I'm preparing the next packet. Once MY packet is ready,
// If the receiver is idle: start preamble, schedule preamble done, go to preamble
// Else: Go to Eager state
if (e.getEventType() == SimulatedEvent.SimEvtType.PACKET_READY && isMyEvent(e)) {
if (rstate == Receiver.IDLE) {
if(transAttempt == 0)
{
packRdyTime = stime.getCurrentTime();
}
scheduleMyEvent(SimulatedEvent.SimEvtType.PREAMBLE_START, 0, false);
scheduleMyEvent(SimulatedEvent.SimEvtType.PREAMBLE_DONE, PREAMBLE_TIME, true);
tstate = Transmitter.PREAMBLE;
} else {
tstate = Transmitter.EAGER;
}
}
break;
case WAITING:
//If I'm waiting the backoff slots, and I've tried way too many times, abort and move on to the next packet, schedule when its done, prepare
if (e.getEventType() == SimulatedEvent.SimEvtType.BACKOFF_DONE && isMyEvent(e)) {
// if we have attempted to transmit this packet more than 15 times, it's time to abort it
if (transAttempt >= 15) {
packetsDropped++;
// Packet is Aborted
scheduleMyEvent(SimulatedEvent.SimEvtType.PACKET_ABORTED, 0, true);
//packetsToSend--;
transAttempt = 0;
//if (packetsToSend == 0) {
// tstate = Transmitter.DONE;
//} else {
scheduleMyEvent(SimulatedEvent.SimEvtType.PACKET_READY, RandomProcessTime(), true);
tstate = Transmitter.PREPARING;
//}
}
// if the receiver is idle, then start the preamble. Otherwise go to the eager transmitter state
else
{
if (rstate == Receiver.IDLE) {
scheduleMyEvent(SimulatedEvent.SimEvtType.PREAMBLE_START, 0, false);
scheduleMyEvent(SimulatedEvent.SimEvtType.PREAMBLE_DONE, PREAMBLE_TIME, true);
tstate = Transmitter.PREAMBLE;
} else {
tstate = Transmitter.EAGER;
}
}
}
break;
}
}
public enum Transmitter {
EAGER, PREPARING, PREAMBLE, SENDING, JAMMING, WAITING, DONE
}
public enum Receiver {
BUSY, GAP, IDLE
}
}