/** * Jeff Rupp * Master's Thesis * 2005 * * * A simple straight through protocol, for fastest simulation runs when the protocol * isn't an important consideration. * */ package jdr.mobisim; import java.util.*; import java.awt.*; import org.apache.log4j.*; public class Protocol_Straight extends ProtocolIF implements EventCallbackIF { private static Logger m_logger = Logger.getLogger(Protocol_Straight.class); public static final int s_RESPONSE_TIMEOUT_REAL_MSEC_MULTIPLIER = 5; // actual PC's CPU mSec timeout waiting for a response public static final int s_RESPONSE_TIMEOUT_SIM_TICS = 3000; // sim tics timeout waiting for a response // this must be set to false during normal runs, or the logging will slow the // simulation horribly private static final boolean DO_QUEUE_LOGS = false; private static final boolean DO_EXCESSIVE_LOGS = false; private static final boolean DO_SCHEDULER_LOGS = false; private static int s_instanceCount = 0; private int m_instanceNum = 0; protected int m_transmitDestinationNodeNumber = -1; protected int m_historyTransmitDestinationNodeNumber = -1; protected long m_lastTrasmitTime = 0; private int m_expectedSequenceNumber = -1; private int m_seqNumToUseAsAck = -1; // the protocol's own queue protected Vector m_incomingPackets = new Vector(); protected java.lang.Object m_incomingQueueLock = new java.lang.Object(); private Vector m_nodesHeard = new Vector(); private EventCallbackIF m_nodeEventCallbackIf = null; private EDU.oswego.cs.dl.util.concurrent.Semaphore m_sem = new EDU.oswego.cs.dl.util.concurrent.Semaphore(0); private EDU.oswego.cs.dl.util.concurrent.Mutex m_lockTransmit = new EDU.oswego.cs.dl.util.concurrent.Mutex(); public Protocol_Straight() { m_instanceNum = s_instanceCount++; } public void setNodeEventCallbackIf(EventCallbackIF nodeEvtIf) { m_nodeEventCallbackIf = nodeEvtIf; } public boolean AddPacket(PacketIF packet) { boolean addedToIncomingQueue = false; // need to get the source node number for logging byte [] data = packet.getData(); int sourceNode = MobilePacket.extractInt(data, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int destinationNode = MobilePacket.extractInt(data, MobilePacket.DESTINATION_NODE_BYTE_OFFSET); byte messageType = data[MobilePacket.MESSAGE_TYPE_BYTE_OFFSET]; int ackNumber = MobilePacket.extractInt(data, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET); int seqNumber = MobilePacket.extractInt(data, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET); if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " wants packet added to incoming queue from node: " + sourceNode); } long simTimeNow = Scheduler.getInstance().GetSimulationTime(); switch(messageType) { case MobilePacket.INSIDE_CLUSTER: case MobilePacket.BETWEEN_CLUSTER: case MobilePacket.CHOOSE_HEADS: case MobilePacket.TELL_HEAD: case MobilePacket.REJECT_HEAD: case MobilePacket.RTS: case MobilePacket.CTS: case MobilePacket.ACK: if((destinationNode == -1) || (destinationNode == m_myNodeNumber)) { synchronized(m_packetQueueLock) { m_receivedPackets.addElement(packet); Scheduler.getInstance().ScheduleEvent(m_nodeEventCallbackIf, DO_SCHEDULER_LOGS); if(DO_QUEUE_LOGS) { m_logger.debug("node: "+m_myNodeNumber + " added packet to queue from node: " + sourceNode); } } } break; default: if((destinationNode == -1) || (destinationNode == m_myNodeNumber)) { synchronized(m_packetQueueLock) { m_receivedPackets.addElement(packet); Scheduler.getInstance().ScheduleEvent(m_nodeEventCallbackIf, DO_SCHEDULER_LOGS); if(DO_QUEUE_LOGS) { m_logger.debug("protocol passing packet of type: "+ messageType + " on to node: "+m_myNodeNumber+ " packet added to queue"); } } } break; } return addedToIncomingQueue; } /** * scheduler callback method, to get our own thread to do the transmit in */ public boolean ExecuteEvent(long timeNow) { m_logger.error("not using this for the Protocol_Straight class"); return true; } /** * gets all the nodes in range of the transmitting node and sends the packet on * to them * @param packet The data to transmit * @param source the location of the source node * @param powerLevel the dBm to transmit at, this is used for the distance transmitted * * @return true if transmit was successful */ public boolean TransmitData(long xmitTime, int lengthInBits, PacketIF packet, jdr.utils.FloatPoint source, double powerLeveldBm) { return TransmitData(xmitTime, lengthInBits, packet, source, powerLeveldBm, false); } public boolean TransmitData(long xmitTime, int lengthInBits, PacketIF packet, jdr.utils.FloatPoint source, double powerLeveldBm, boolean doLogs) { boolean rc = false; boolean transmittedOK = false; long timePassed = 0; try { while(!transmittedOK) { long startTransmitTimeoutTime = xmitTime; int startTransmitTimeoutTics = 10; while(!m_lockTransmit.attempt(100)) { if((timePassed) > s_RESPONSE_TIMEOUT_SIM_TICS) { m_logger.error("Protocol_Straight instance: "+m_instanceNum+" failed to lock transmit"); timePassed = 0; } Scheduler.getInstance().IncrementSimulationTics(startTransmitTimeoutTime, startTransmitTimeoutTics); timePassed += startTransmitTimeoutTics; startTransmitTimeoutTime += startTransmitTimeoutTics; } PropagationIF prop = PropagationIF.getInstance(); // first check the carrier detect to see if anybody else is transmitting //??? while(prop.CarrierDetect(xmitTime, source)) { // detected another carrier, backoff a little random amount //??? xmitTime += GetNextBackoff(); } byte [] xmitData = packet.getData(); int sourceNode = packet.extractInt(xmitData, MobilePacket.SOURCE_NODE_BYTE_OFFSET); int destNode = packet.extractInt(xmitData, MobilePacket.DESTINATION_NODE_BYTE_OFFSET); m_transmitDestinationNodeNumber = destNode; m_historyTransmitDestinationNodeNumber = destNode; m_lastTrasmitTime = xmitTime; m_expectedSequenceNumber = PacketIF.GetNextSequenceNumber(); byte [] sendData = packet.getData(); packet.insertInt(sendData, MobilePacket.SEQUENCE_NUMBER_BYTE_OFFSET, m_expectedSequenceNumber); packet.insertInt(sendData, MobilePacket.ACK_SEQUENCE_NUMBER_BYTE_OFFSET, m_seqNumToUseAsAck); // do the transmit rc = prop.TransmitData(xmitTime,lengthInBits,packet,source,powerLeveldBm,doLogs); double xmitWatts = (Math.pow(10.0, (powerLeveldBm / 10))) / 1000; // next calculate what portion of an hour based on the packet size and // our data rate. xmitWatts *= (packet.getTransmitedBitCount() / m_dataRateBps) / s_SECONDS_PER_HOUR; packet.IncrementPowerConsumedInTransit(xmitWatts); IncrementWattsConsumed(xmitWatts); m_lockTransmit.release(); transmittedOK = true; } } catch(java.lang.InterruptedException iex) { m_logger.error("Protocol_Straight instance: "+m_instanceNum+ " lock transmit acquire got exception", iex); } m_transmitDestinationNodeNumber = -1; return rc; } public void AddNodeHeard(NodeHeard hrd) { synchronized(m_nodesHeard) { m_nodesHeard.addElement(hrd); } } protected byte GetMinTransmitLevel(int sourceNode) { byte xmitLevel = 0; // determine the transmit level needed to communicate with the // node we want to communicate with synchronized(m_nodesHeard) { int numHrd = m_nodesHeard.size(); for(int i = 0; i < numHrd; ++i) { NodeHeard testHrd = ((NodeHeard)m_nodesHeard.get(i)); if(testHrd.m_nodeNumber == sourceNode) { xmitLevel = testHrd.m_lowestLevelHeard; break; } } } if(DO_EXCESSIVE_LOGS) { m_logger.debug("node: " + m_myNodeNumber +" heard node: "+sourceNode + " at level: "+xmitLevel); } return xmitLevel; } public PacketIF GetNextPacket() { synchronized(m_incomingQueueLock) { return (PacketIF)m_incomingPackets.get(0); } } public boolean conversationInProgress() { boolean imTalkingNow = true; try { imTalkingNow = !m_lockTransmit.attempt(0); if(!imTalkingNow) { m_lockTransmit.release(); } } catch(InterruptedException iex) { m_logger.error("Protocol_Straight instance: "+m_instanceNum+ " interrupted acquiring transmit lock"); } return imTalkingNow; } public int getNodeNumber() { return m_myNodeNumber; } public void Draw(Graphics2D graphics, Rectangle bounds, double xScale, double yScale) { int destX = 0; int destY = 0; Color origColor = graphics.getColor(); Color lineColor = Color.red; NodeIF destnode = null; if(conversationInProgress()) { destnode = AllNodes.Instance().GetNodeByNumber(m_transmitDestinationNodeNumber); lineColor = Color.magenta; } else if(m_transmitDestinationNodeNumber != -1) // some sort of conversation going on { destnode = AllNodes.Instance().GetNodeByNumber(m_transmitDestinationNodeNumber); lineColor = Color.yellow; } else if(System.getProperty("jdr.DrawLastTransmitInterval") != null) { int lastXmitInterval = Integer.parseInt(System.getProperty("jdr.DrawLastTransmitInterval")); if((Scheduler.getInstance().GetSimulationTime() - m_lastTrasmitTime) <= lastXmitInterval) { destnode = AllNodes.Instance().GetNodeByNumber(m_historyTransmitDestinationNodeNumber); lineColor = Color.blue; } } else if(System.getProperty("jdr.DrawLastTransmit") != null) { destnode = AllNodes.Instance().GetNodeByNumber(m_historyTransmitDestinationNodeNumber); lineColor = Color.white; } if(destnode != null) { jdr.utils.FloatPoint loc = destnode.getLocation(); destX = (int)(loc.getX() / xScale) + 5; // add an offset, so concurrent lines destY = (int)(loc.getY() / yScale) + 5; // between 2 nodes aren't on top of each other graphics.setColor(lineColor); int srcX = (int)((m_location.getX() / xScale)); int srcY = (int)((m_location.getY() / yScale)); graphics.drawLine(srcX, srcY, destX, destY); graphics.drawString("S", srcX, srcY); // draw an arrow 1/2 way along the line to indicate direction graphics.setColor(origColor); } } public String toString() { return "Protocol_Straight node number: " + m_myNodeNumber; } public String DumpPacketQueue() { String logStr = ""; logStr += super.DumpPacketQueue(); return logStr; } //============================================================================ // inner classes //============================================================================ } // end class definition