/* Controls the sequence of events involved in initiating a session.
Copyright (c) 2004-2005 The Regents of the University of California.
All rights reserved.
Permission is hereby granted, without written agreement and without
license or royalty fees, to use, copy, modify, and distribute this
software and its documentation for any purpose, provided that the above=
copyright notice and the following two paragraphs appear in all copies
of this software.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.
PT_COPYRIGHT_VERSION_2
COPYRIGHTENDKEY
*/
package ptolemy.domains.wireless.lib.network.mac;
import java.util.LinkedList;
import ptolemy.actor.TypedIOPort;
import ptolemy.actor.util.Time;
import ptolemy.data.BooleanToken;
import ptolemy.data.DoubleToken;
import ptolemy.data.IntToken;
import ptolemy.data.RecordToken;
import ptolemy.data.Token;
import ptolemy.data.expr.Variable;
import ptolemy.data.type.BaseType;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.NamedObj;
////////////////////////////////////////////////////////////////////////=
//
//// TXCoordinationSta
/**
TXCoordination class is responsible for initiating a session. After a packet
arrives from the network layer, TXCoordination will generate RTS if the packet
is long enough. It will send RTS to the destination and wait for CTS. If CTS
is received within a specified interval, data will be sent after SIFS seconds.
If ACK is received within a given time after the data is sent, the session
is complete. TXCoordination will go to backoff before handling the next packet
in the queue.If the network packet is not long enough, RTS/CTS will not be used
to reduce overhead. In either case, Carrier sense is only done for the first
message in the sequence, which is RTS, retransmitted data or data if RTS is not
used. TXCoordination gets the channel status by sending a backoff signal with
count 0 to the Backoff process in the Transmission block.If the channel turns
to be busy, TXCoordination will send another backoff signal with count -1 to
start the real backoff. If CTS or ACK is not received in time, the correspondng
RTS or data will neeed to be retransmitted. TXCoordination will increase the
corresponding retry counters. The backoff window size is also exponentially
increased Retransmission will not start until another backoff is completed to
avoid congestions.
@author Charlie Zhong, Yang Zhao
@version TxCoordination.java,v 1.13 2004/04/22 19:46:17 ellen_zh Exp
@since Ptolemy II 4.0
@Pt.ProposedRating Red (czhong)
@Pt.AcceptedRating Red (reviewmoderator)
*/
public class TxCoordination extends MACActorBase {
/** Construct an actor with the specified name and container.
* The container argument must not be null, or a
* NullPointerException will be thrown.
* If the name argument is null, then the name is set to the empty =
string.
* This constructor write-synchronizes on the workspace.
* @param container The container.
* @param name The name of the actor.
* @exception IllegalActionException If the container is incompatib=
le
* with this actor.
* @exception NameDuplicationException If the name coincides with
* an actor already in the container.
*/
public TxCoordination(CompositeEntity container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
// create ports
PduRequest = new TypedIOPort(this, "PduRequest", true, false);
PduRequest.setTypeEquals(BaseType.GENERAL);
TXTXConfirm = new TypedIOPort(this, "TXTXConfirm", true, false);
TXTXConfirm.setTypeEquals(BaseType.GENERAL);
BkDone = new TypedIOPort(this, "BkDone", true, false);
BkDone.setTypeEquals(BaseType.GENERAL);
GotAck = new TypedIOPort(this, "GotAck", true, false);
GotAck.setTypeEquals(BaseType.GENERAL);
fromPowerControl = new TypedIOPort(this, "fromPowerControl", true,
false);
fromPowerControl.setTypeEquals(BaseType.GENERAL);
getBackoff = new TypedIOPort(this, "getBackoff", false, true);
getBackoff.setTypeEquals(BaseType.GENERAL);
TXTXRequest = new TypedIOPort(this, "TXTXRequest", false, true);
TXTXRequest.setTypeEquals(BaseType.GENERAL);
overhead = new TypedIOPort(this, "overhead", false, true);
overhead.setTypeEquals(BaseType.DOUBLE);
}
///////////////////////////////////////////////////////////////////
//// public variables ////
/** Port receiving messages from the network layer
*/
public TypedIOPort PduRequest;
/** Port receiving confirmation from the Transmission block
*/
public TypedIOPort TXTXConfirm;
/** Port receiving backoff complete notification from the Backoff process
*/
public TypedIOPort BkDone;
/** Port receiving the notification of the receipt of either Ack or Cts
* from the RxCoordination process
*/
public TypedIOPort GotAck;
/** Port receiving messages from the PowerControl block
*/
public TypedIOPort fromPowerControl;
/** Port sending the backoff request to the Backoff process
*/
public TypedIOPort getBackoff;
/** Port sending the TX request to the Transmission block
*/
public TypedIOPort TXTXRequest;
/** Port sending overhead of handling a pdu request.
*/
public TypedIOPort overhead;
///////////////////////////////////////////////////////////////////
//// public methods ////
public void fire() throws IllegalActionException {
super.fire();
int kind = whoTimeout(); // check if a timer times out and which
Time currentTime = getDirector().getModelTime();
boolean isNetData = false;
if (PduRequest.hasToken(0)) {
isNetData = true;
RecordToken msg = (RecordToken) PduRequest.get(0);
if (_txQueue.size() >= QueueSize) {
if (_debugging) {
_debug("Queue is full.");
}
} else {
_txQueue.add(msg);
_pduTime.add(currentTime);
}
}
switch (_currentState) {
case TxC_Idle:
boolean backoff = true;
if ((_mBkIP != null) && _mBkIP instanceof Variable) {
Token token = ((Variable) _mBkIP).getToken();
backoff = ((BooleanToken) token).booleanValue();
} //FIXME: assume it is instanceof variable.
if (isNetData && !backoff) {
_handleData();
} else if (BkDone.hasToken(0)) {
BkDone.get(0);
_checkQueue();
}
break;
case Wait_Rts_Backoff:
if (BkDone.hasToken(0)) {
RecordToken BkDoneMsg = (RecordToken) BkDone.get(0);
if (((IntToken) BkDoneMsg.get("cnt")).intValue() == -2) {
TXTXRequest.send(0, _rtsdu);
_currentState = Wait_Rts_Sent;
} else // channel is busy, need to backoff
{
_backoff(_ccw, -1);
// modified standard here
_cont = true;
_currentState = TxC_Backoff;
}
}
break;
case Wait_Rts_Sent:
if (TXTXConfirm.hasToken(0)) {
TXTXConfirm.get(0);
_Trsp = setTimer(Timeout, currentTime.add(_CTSTimeout * 1e-6));
_currentState = Wait_Cts;
}
break;
case Wait_Cts:
if (kind == Timeout) {
if (_ccw != _aCWmax) {
_ccw = (2 * _ccw) + 1;
}
// backoff before retry
_backoff(_ccw, -1);
// need to reset it!!!
_slrc++;
if (_slrc == _dot11LongRetryLimit) {
_ccw = _aCWmin;
// modified standard here
_slrc = 0;
_cont = false;
} else {
_cont = true;
}
_currentState = TxC_Backoff;
} else if (GotAck.hasToken(0)) {
RecordToken GotCtsMsg = (RecordToken) GotAck.get(0);
if (((IntToken) GotCtsMsg.get("kind")).intValue() == GotCts) {
cancelTimer(_Trsp);
Time endRx = new Time(getDirector(),
((DoubleToken) GotCtsMsg.get("endRx"))
.doubleValue());
_ssrc = 0;
setTimer(SifsTimeout, endRx.add(_dSifsDly * 1e-6));
int durId = _aSifsTime + _aPreambleLength
+ _aPlcpHeaderLength + (_sAckCtsLng / _mBrate);
_setDurIdField(_tpdu, durId);
_currentState = Wait_Cts_Sifs;
}
}
break;
case Wait_Cts_Sifs:
if (kind == SifsTimeout) {
_sendTxRequest();
}
break;
case Wait_Mpdu_Backoff:
if (BkDone.hasToken(0)) {
RecordToken BkDoneMsg = (RecordToken) BkDone.get(0);
if (((IntToken) BkDoneMsg.get("cnt")).intValue() == -2) {
_sendTxRequest();
} else {
_backoff(_ccw, -1);
// modified standard here
_cont = true;
_currentState = TxC_Backoff;
}
}
break;
case Wait_Pdu_Sent:
if (TXTXConfirm.hasToken(0)) {
TXTXConfirm.get(0);
// no need to wait for ACK for broadcast
int Addr1 = ((IntToken) _tpdu.get("Addr1")).intValue();
if (Addr1 == mac_broadcast_addr) {
_ssrc = 0;
_slrc = 0;
_ccw = _aCWmin;
_cont = false;
_backoff(_ccw, -1);
_currentState = TxC_Backoff;
} else {
_Trsp = setTimer(Timeout, currentTime
.add(_CTSTimeout * 1e-6));
_currentState = Wait_Ack;
}
}
break;
case Wait_Ack:
if (kind == Timeout) {
if (_ccw != _aCWmax) {
_ccw = (2 * _ccw) + 1;
}
// backoff before retry
_backoff(_ccw, -1);
_ssrc++;
//set retryBit=1;
_setRetryField(_tpdu, 1);
if (_ssrc == _dot11ShortRetryLimit) {
_ccw = _aCWmin;
// modified standard here
_ssrc = 0;
_cont = false;
} else {
_cont = true;
}
_currentState = TxC_Backoff;
} else if (GotAck.hasToken(0)) {
//NOTE: Charlie, you need to get the token here to
//consume it. Otherwise, there is a deadlock at
//this time.
GotAck.get(0);
_ssrc = 0;
_slrc = 0;
_ccw = _aCWmin;
_cont = false;
_backoff(_ccw, -1);
_currentState = TxC_Backoff;
}
break;
case TxC_Backoff:
if (BkDone.hasToken(0)) {
BkDone.get(0);
}
if (_cont) {
_cont = false;
_sendFrag();
} else {
_checkQueue();
}
break;
}
}
/** Initialize the private variables.
* @exception IllegalActionException If thrown by the base class.
*/
public void initialize() throws IllegalActionException {
super.initialize();
_txQueue = new LinkedList();
_pduTime = new LinkedList();
_currentState = 0;
_dSifsDly = _aSifsTime - _aRxTxTurnaroundTime;
_ssrc = 0;
_slrc = 0;
_ccw = _aCWmin;
_seqNum = 0;
_CTSTimeout = _aSifsTime + _aPreambleLength + _aPlcpHeaderLength
+ _aSlotTime + (_sAckCtsLng / _mBrate);
NamedObj macComposite = getContainer().getContainer();
if (macComposite.getAttribute("mBkIP") != null) {
_mBkIP = macComposite.getAttribute("mBkIP");
} else {
_mBkIP = null;
throw new IllegalActionException("the MAC compositor "
+ "dosen't contain a parameter named mBkIP");
}
// randomize node's time to go to TxC_Idle
_backoff(_ccw, -1);
}
///////////////////////////////////////////////////////////////////
//// private methods ////
private RecordToken _createPacket(int subtype, int duration, int RA, int TA)
throws IllegalActionException {
Token[] DataPacketValues = { new IntToken(0),
new IntToken(ControlType), new IntToken(subtype),
new IntToken(0), new IntToken(0), new IntToken(0),
new IntToken(0), new IntToken(0), new IntToken(0),
new IntToken(0), new IntToken(0), new IntToken(123),
new IntToken(duration), new IntToken(RA), new IntToken(TA),
new IntToken(160) };
RecordToken pkt = new RecordToken(RtsPacket, DataPacketValues);
return (pkt);
}
private RecordToken _createDataPacket(RecordToken msg, int dest_addr)
throws IllegalActionException {
Token[] DataPacketValues = {
new IntToken(0),
new IntToken(DataType),
new IntToken(Data),
new IntToken(0),
new IntToken(0),
new IntToken(0),
new IntToken(0),
new IntToken(0),
new IntToken(0),
new IntToken(0),
new IntToken(0),
new IntToken(123),
new IntToken(_aSifsTime + _aPreambleLength + _aPlcpHeaderLength
+ (_sAckCtsLng / _mBrate)),
new IntToken(dest_addr),
new IntToken(getID()),
new IntToken(0),
new IntToken(_seqNum - (_seqNum / 4096 * 4096)),
new IntToken(0),
new IntToken(0),
msg,
new IntToken((34 * 8)
+ ((IntToken) msg.get("Length")).intValue()) };
_seqNum++;
RecordToken pkt = new RecordToken(DataPacket, DataPacketValues);
return (pkt);
}
private RecordToken _setRetryField(RecordToken msg, int retryBit)
throws IllegalActionException {
Token[] DataPacketValues = { new IntToken(0), new IntToken(DataType),
new IntToken(Data), new IntToken(0), new IntToken(0),
new IntToken(0), new IntToken(retryBit), new IntToken(0),
new IntToken(0), new IntToken(0), new IntToken(0),
new IntToken(123), msg.get("durId"), msg.get("Addr1"),
msg.get("Addr2"), new IntToken(0), msg.get("SeqNum"),
new IntToken(0), new IntToken(0), msg.get("payload"),
msg.get("Length") };
RecordToken pkt = new RecordToken(DataPacket, DataPacketValues);
return (pkt);
}
private RecordToken _setDurIdField(RecordToken msg, int durId)
throws IllegalActionException {
Token[] DataPacketValues = { new IntToken(0), new IntToken(DataType),
new IntToken(Data), new IntToken(0), new IntToken(0),
new IntToken(0), msg.get("retryBit"), new IntToken(0),
new IntToken(0), new IntToken(0), new IntToken(0),
new IntToken(123), new IntToken(durId), msg.get("Addr1"),
msg.get("Addr2"), new IntToken(0), msg.get("SeqNum"),
new IntToken(0), new IntToken(0), msg.get("payload"),
msg.get("Length") };
RecordToken pkt = new RecordToken(DataPacket, DataPacketValues);
return (pkt);
}
private void _backoff(int ccw, int cnt) throws IllegalActionException {
Token[] getBackoffMsgValues = { new IntToken(Backoff),
new IntToken(ccw), new IntToken(cnt) };
RecordToken event = new RecordToken(getBackoffMsgFields,
getBackoffMsgValues);
getBackoff.send(0, event);
}
private void _sendTxRequest() throws IllegalActionException {
Token[] TxRequestMsgValues = { new IntToken(TxRequest), _tpdu,
new IntToken(_mBrate * (int) 1e6) };
RecordToken copyTpdu = new RecordToken(TxRequestMsgFields,
TxRequestMsgValues);
TXTXRequest.send(0, copyTpdu);
Time delay = getDirector().getModelTime().subtract(_time);
overhead.send(0, new DoubleToken(delay.getDoubleValue() * 1e6));
_currentState = Wait_Pdu_Sent;
}
private void _sendFrag() throws IllegalActionException {
// just to see if channel is idle
_backoff(0, 0);
// no RTS is needed for broadcast
int length = ((IntToken) _tpdu.get("Length")).intValue();
int retryBit = ((IntToken) _tpdu.get("retryBit")).intValue();
int Addr1 = ((IntToken) _tpdu.get("Addr1")).intValue();
if ((length > _dotllRTSThreshold) && (retryBit == 0)
&& (Addr1 != mac_broadcast_addr)) {
_currentState = Wait_Rts_Backoff;
} else {
_currentState = Wait_Mpdu_Backoff;
}
}
private void _handleData() throws IllegalActionException {
int dest_addr;
_time = (Time) _pduTime.removeFirst();
RecordToken msg = (RecordToken) _txQueue.removeFirst();
int msg_kind = ((IntToken) msg.get("kind")).intValue();
switch (msg_kind) {
case netw_interest_msg:
case netw_data_msg:
dest_addr = ((IntToken) msg.get("toMACAddr")).intValue();
break;
default: // everything else is broadcast
dest_addr = mac_broadcast_addr;
}
_tpdu = _createDataPacket(msg, dest_addr);
int length = ((IntToken) _tpdu.get("Length")).intValue();
int Addr1 = ((IntToken) _tpdu.get("Addr1")).intValue();
int durId = (3 * (_aSifsTime + _aPreambleLength + _aPlcpHeaderLength))
+ ((length + (2 * _sAckCtsLng)) / _mBrate);
// no RTS is needed for broadcast
if ((length <= _dotllRTSThreshold) || (Addr1 == mac_broadcast_addr)) {
//Note: Charlie, you didn't surround this if with {}, which causes the
//else block below related to the if below. There are several of this kind
// of errors...
if (_debugging) {
_debug("RTS is not sent.");
}
} else {
RecordToken pdu = _createPacket(Rts, durId, dest_addr, getID());
Token[] TxRequestMsgValues = { new IntToken(TxRequest), pdu,
new IntToken(_mBrate * (int) 1e6) };
_rtsdu = new RecordToken(TxRequestMsgFields, TxRequestMsgValues);
}
_sendFrag();
}
private void _checkQueue() throws IllegalActionException {
if (_txQueue.size() > 0) {
_handleData();
} else {
_currentState = TxC_Idle;
}
}
///////////////////////////////////////////////////////////////////
//// private variables ////
private int _dSifsDly;
///////////////////////////////////////////////////////////////////
//// private variables ////
private int _ccw;
///////////////////////////////////////////////////////////////////
//// private variables ////
private int _slrc;
///////////////////////////////////////////////////////////////////
//// private variables ////
private int _ssrc;
///////////////////////////////////////////////////////////////////
//// private variables ////
private int _CTSTimeout;
///////////////////////////////////////////////////////////////////
//// private variables ////
private int _seqNum;
private boolean _cont;
private RecordToken _rtsdu;
private RecordToken _tpdu;
private LinkedList _txQueue;
private Timer _Trsp;
//This list saves the time when each pdu request is generated. It is used
//to calculate the overheah for statistic perpose. Yang
private LinkedList _pduTime;
private Time _time;
// define states in FSM
private static final int TxC_Idle = 0;
private static final int Wait_Mpdu_Backoff = 1;
private static final int Wait_Pdu_Sent = 2;
private static final int Wait_Ack = 3;
private static final int TxC_Backoff = 4;
private static final int Wait_Rts_Backoff = 5;
private static final int Wait_Rts_Sent = 6;
private static final int Wait_Cts = 7;
private static final int Wait_Cts_Sifs = 8;
private int _currentState = 0;
private static final int Timeout = 1;
private static final int SifsTimeout = 2;
private static final int QueueSize = 100;
}