EPONImplementationforOMNet++  0.8Beta
ONUQPerLLiDBase Class Reference

ONU_Q_mgmt_PerLLiD creates on queue per LLID. More...

#include <ONUQPerLLiDBase.h>

Inheritance diagram for ONUQPerLLiDBase:
ONU_QPL_RR

List of all members.

Public Member Functions

 ONUQPerLLiDBase ()
virtual ~ONUQPerLLiDBase ()
virtual void requestPacket ()=0
 The queue should send a packet whenever this method is invoked.
virtual MPCPReportrequestMPCP_REPORT ()
 Return an MPCP REPORT with the status of the queues.
virtual uint16_t getMPCPRepSize ()
 Return the MPCP Report Size in Bytes.
virtual bool isEmpty ()

Protected Types

typedef std::vector< QueuePerLLidPonQueues
 Vector of QueuePerLLid's, vector Because we need them sorted by priority but map will sort by llid (<-unique)

Protected Member Functions

virtual void initialize (int stage)
virtual int numInitStages () const
virtual void finish ()
virtual void handleMessage (cMessage *msg)
virtual void processFrameFromHigherLayer (cMessage *msg)
 Insert the new message to the proper Q and notify the lower layer.
virtual void processFrameFromLowerLayer (cMessage *msg)
virtual bool checkLLIDforUs (cMessage *msg)
virtual int getIndexForLLID (uint16_t llid)
 Search per LLID and get the index of the queue for this LLID.
virtual int getDefaultLLiD ()
 Return the first LLiD flagged as default.
virtual void addSortedLLID (QueuePerLLid tmp_qpllid)
 Adds a new queue to the PonQueues vector in the proper order based on the service priority.
virtual void checkIfAllEmpty ()
 Check is all the Qs are empty.
virtual int getIndexForService (std::string name)
 Get queue from service name.
virtual void processMPCP (EthernetIIFrame *frame)
virtual void sendMPCPReg ()
virtual void startMPCPReg (uint32_t regMaxRandomSleep)
 Initializes the MPCP registration process and sets the regTOMsg (TimeOut for the registration)
cModule * findModuleUp (const char *name)

Protected Attributes

double regTimeOut
int numOfLLIDs
bool allQsEmpty
bool allQsBlocked
int nextQIndex
int queueLimit
double granularity
 Granularity of QoS limit (i.e. per second <- most common)
SrvListserviceList
PonQueues pon_queues
cModule * olt_mac
 The OLT MAC module for direct delivery.
MACAddress opt_mac
 The Current ONU Optical MAC Address.
cMessage * regTOMsg
cMessage * regSendMsg

Detailed Description

ONU_Q_mgmt_PerLLiD creates on queue per LLID.

Here is actually done all the bandwidth allocation and timer calculation. Also this class handles the ONU registration and the updates the ONU table. Thus some MPCP messages are generated from here and timeouts are defined here. Now since all the time management is done here... we have to define the number of time slots and the time slot duration. Finally this class is aware of any services in this network and thus has a pointer to the SrvList class (if any module is available in the scenario else it is NULL).

The whole idea is to extend this class and create your own bandwidth allocation algorithms. The default behavior of this class is to apply round robin on the queues (per frame). The only thing considered is the priority (not strictly) and happens because the queues are sorted based on the service priority.

Basic methods that would be useful to override when you extend are explained below.

UPDATE: Registration process is now done with sendDirectly to avoid collisions


Member Typedef Documentation

typedef std::vector<QueuePerLLid> ONUQPerLLiDBase::PonQueues [protected]

Vector of QueuePerLLid's, vector Because we need them sorted by priority but map will sort by llid (<-unique)


Constructor & Destructor Documentation

                                {
      regTOMsg = new cMessage("regTOMsg", REGTOMSG);
      regSendMsg = new cMessage("regSendMsg", REGSENDMSG);

      allQsBlocked=false;
      allQsEmpty=true;
      nextQIndex=0;
}
                                 {
      cancelAndDelete(regSendMsg);
      cancelAndDelete(regTOMsg);

      for (uint32_t i=0; i<pon_queues.size(); i++){
            pon_queues[i].clean();
      }
      pon_queues.clear();
}

Member Function Documentation

void ONUQPerLLiDBase::addSortedLLID ( QueuePerLLid  tmp_qpllid) [protected, virtual]

Adds a new queue to the PonQueues vector in the proper order based on the service priority.

                                                          {
      // Just add it if empty...
      if (pon_queues.empty()) {
            pon_queues.push_back(tmp_qpllid);
            return;
      }

      // Add in the proper possition
      for (PonQueues::iterator it = pon_queues.begin(); it != pon_queues.end(); it++){
            if ((*it).prior < tmp_qpllid.prior){
                  pon_queues.insert(it, tmp_qpllid);
                  return;
            }
      }

      // If not added till here... add to the end
      pon_queues.push_back(tmp_qpllid);
}
void ONUQPerLLiDBase::checkIfAllEmpty ( ) [protected, virtual]

Check is all the Qs are empty.

IF yes set the all empty variable. This must be done after the frame request from the lower layer.

                                     {
      if (pon_queues.size() == 0) allQsEmpty = true;
      if (pon_queues[0].isEmpty()
                  && pon_queues.size() == 1) allQsEmpty = true;


      bool found=false;
      // if the current Q is empty -> find the next one
      if (pon_queues[nextQIndex].isEmpty()){
            for (int i=0; i<(int)pon_queues.size(); i++){
                  if (!pon_queues[i].isEmpty()) {
                        found=true;
                        break;
                  }
            }
      // If not empty.. we have a frame
      }else found=true;

      allQsEmpty = !found;
}
bool ONUQPerLLiDBase::checkLLIDforUs ( cMessage *  msg) [protected, virtual]
                                                 {
      // Drop if it is not for us based on LLID
      EPON_LLidCtrlInfo * nfo =  dynamic_cast<EPON_LLidCtrlInfo *>(msg->getControlInfo());
      // Check for BC
      if (nfo && nfo->llid != LLID_EPON_BC){
            // -1 = No such llid
            if (getIndexForLLID(nfo->llid) == -1) {
                  return false;
            }
      }
      return true;
}
cModule * ONUQPerLLiDBase::findModuleUp ( const char *  name) [protected]
                                                        {
      cModule *mod = NULL;
      for (cModule *curmod=this; !mod && curmod; curmod=curmod->getParentModule())
           mod = curmod->getSubmodule(name);
      return mod;
}
void ONUQPerLLiDBase::finish ( ) [protected, virtual]
                            {
      simtime_t t = simTime();

      for (uint32_t i=0; i<pon_queues.size(); i++){
            std::string srvName=pon_queues[i].getServiceName();
            std::string name=srvName+" BytesDropped";
            recordScalar(name.c_str(), pon_queues[i].vec->numBytesDropped  );
            name=srvName+" BytesSent";
            recordScalar(name.c_str(), pon_queues[i].vec->numBytesSent  );

            if (t>0)
            {
                  name=srvName+" bytes dropped/sec";
                  unsigned long dropped = pon_queues[i].vec->numBytesDropped;
                  recordScalar(name.c_str(), dropped/t);
                  name=srvName+" bytes sent/sec";
                  unsigned long sent = pon_queues[i].vec->numBytesSent;
                  recordScalar(name.c_str(), sent/t);
                  name=srvName+" Drop PerCent";
                  if (dropped+sent  == 0)
                        recordScalar(name.c_str(), 0.0);
                  else
                        recordScalar(name.c_str(), (double)100*((double)dropped/(dropped+sent)));
            }
      }
}
int ONUQPerLLiDBase::getDefaultLLiD ( ) [protected, virtual]

Return the first LLiD flagged as default.

                                   {
      for (uint32_t i=0; i<pon_queues.size(); i++)
            if (pon_queues[i].isDefault) return pon_queues[i].llid;

      return LLID_EPON_BC;
}
int ONUQPerLLiDBase::getIndexForLLID ( uint16_t  llid) [protected, virtual]

Search per LLID and get the index of the queue for this LLID.

Useful for en-queuing frames from the higher layers.

                                                 {
      for (uint32_t i=0; i<pon_queues.size(); i++)
            if (pon_queues[i].llid == llid) return i;

      return -1;
}
int ONUQPerLLiDBase::getIndexForService ( std::string  name) [protected, virtual]

Get queue from service name.

                                                     {
      for (uint32_t i=0; i<pon_queues.size(); i++)
                  if (pon_queues[i].getServiceName() == name)
                        return i;

      return -1;
}
uint16_t ONUQPerLLiDBase::getMPCPRepSize ( ) [virtual]

Return the MPCP Report Size in Bytes.

                                        {
      return MPCP_HEADER_LEN+1+pon_queues.size()*2;
}
void ONUQPerLLiDBase::handleMessage ( cMessage *  msg) [protected, virtual]
{

      // Self Message
      if (msg->isSelfMessage())
      {
            EV << "Self-message " << msg << " received\n";

            if (msg == regTOMsg){
                  EV << "*** ONUMacCtl: Registration FAILED"<<endl;
            }else if (msg == regSendMsg)
                  sendMPCPReg();
            else
                  error("Unknown self message received!");

            return;
      }


      // Network Message
      cGate *ingate = msg->getArrivalGate();
      EV << "Frame " << msg << " arrived on port " << ingate->getName() << "...\n";

      if (ingate->getId() ==  gate( "lowerLayerIn")->getId()){
            processFrameFromLowerLayer(msg);
      }
      else if (ingate->getId() ==  gate( "upperLayerIn")->getId()){
            // Add the frame to the proper Q
            processFrameFromHigherLayer(msg);
      }else{
            EV << "ONUMacCtl: Message came FROM THE WRONG DIRRECTION???? Dropping\n";
            delete msg;
      }

}
void ONUQPerLLiDBase::initialize ( int  stage) [protected, virtual]

Reimplemented in ONU_QPL_RR.

{
      if (stage == 0){
            numOfLLIDs=1;
            serviceList=NULL;

            if (dynamic_cast<ServiceConfig *>( findModuleUp("serviceConfig")) != NULL){
                  serviceList = &(dynamic_cast<ServiceConfig *>( findModuleUp("serviceConfig"))->srvs);
                  // 1 llid per service
                  numOfLLIDs = serviceList->size();
            }
            // Parameters
            regTimeOut = par("regTimeOut");
            regTimeOut/=1000;
            queueLimit = par("queueLimit");



            // Parameters
            granularity = par("statsGranularity").doubleValue();


            // Create Q for BroadCasts
            QueuePerLLid tmp_qpllid;
            tmp_qpllid.setServiceName("BroadCast");
            tmp_qpllid.prior = 0.0;
            tmp_qpllid.llid = LLID_EPON_BC;
            tmp_qpllid.vec->setGranularity(granularity);
            tmp_qpllid.queueLimit = queueLimit;
            addSortedLLID(tmp_qpllid);

            WATCH_VECTOR(pon_queues);


            // Get olt module
            olt_mac = simulation.getModuleByPath("epon_olt.olt_if.epon_mac");
            if (!olt_mac)
                  opp_error("Cannot get EPON MAC module for registration!");
      }else if (stage == 1){
            EtherMACBase *mac = dynamic_cast<EtherMACBase *>
                  (gate("lowerLayerOut")->getNextGate()->getOwnerModule()->gate("lowerLayerOut")->getNextGate()->getOwnerModule());
            opt_mac = mac->getMACAddress();
      }
}
bool ONUQPerLLiDBase::isEmpty ( ) [virtual]
                             {
      checkIfAllEmpty();
      return allQsEmpty;
}
virtual int ONUQPerLLiDBase::numInitStages ( ) const [inline, protected, virtual]
{return 2;}
void ONUQPerLLiDBase::processFrameFromHigherLayer ( cMessage *  msg) [protected, virtual]

Insert the new message to the proper Q and notify the lower layer.

Notification is needed only in the case that all the queues are empty. This is because MAC layer will probably be in IDLE status and the start Tx period is not scheduled. By notifying the MAC layer we send a WakeUp msg and it will schedule the next Tx period.

                                                              {
      EV << "ONUQPerLLiDBase: Incoming from higher layer...\n";


      EPON_LLidCtrlInfo *nfo = dynamic_cast<EPON_LLidCtrlInfo *>(msg->getControlInfo());

      // If frame has no info or is BC add to the BroadCast Q
      int Q_index=-1;
      if (!nfo){
            Q_index = getIndexForLLID(getDefaultLLiD());
      }else{
            Q_index = getIndexForLLID(nfo->llid);
      }

      // Check if we can forward frame
      if (Q_index == -1){
            EV << "*** WRONG LLID : DROPPING" <<endl;
            delete msg;
            return;
      }

      // Update the incoming rate BEFORE discarding message
      // in case the Q is full.
      cPacket *pkt = dynamic_cast<cPacket *>(msg);
      pon_queues[Q_index].vec->numIncomingBits+=pkt->getBitLength();

      // IF all empty send... WAKE UP AND ENQUEUE
      // if you send the message directly, it will still work
      // But subclasses do not get the chance to inspect Q polices
      // like data rate limit!
      if (allQsEmpty || allQsBlocked){
            allQsEmpty = false;
            allQsBlocked = false;

            EV << "Sending WAKE UP message to MAC"<<endl;
            send(new cMessage("WAKE UP", WAKEUPMSG), "lowerLayerOut");
      }

      // Check that Q is not full
      if (pon_queues[Q_index].length() >= pon_queues[Q_index].queueLimit){
            pon_queues[Q_index].vec->numBytesDropped+=pkt->getByteLength();
            pon_queues[Q_index].vec->recordVectors();
            EV << "Dropping"<<endl;
            delete msg;
            return;
      }else{
            // Record the incoming bytes
            EV << "Enqueue"<<endl;
            pon_queues[Q_index].vec->recordVectors();
      }


      // Finally add to the Q
      pon_queues[Q_index].insert(dynamic_cast<cPacket *>(msg));




}
void ONUQPerLLiDBase::processFrameFromLowerLayer ( cMessage *  msg) [protected, virtual]
                                                             {
      EV << "ONUQPerLLiDBase: Incoming from lower layer...\n";

      EthernetIIFrame * frame = dynamic_cast<EthernetIIFrame *>(msg);


      if (frame && frame->getEtherType() == MPCP_TYPE){
            processMPCP(frame );
            return;
      }

      // Drop if it is not for us based on LLID
      if (!checkLLIDforUs(msg)) {
            delete msg;
            return;
      }

      send(msg,"upperLayerOut");
}
void ONUQPerLLiDBase::processMPCP ( EthernetIIFrame *  frame) [protected, virtual]

+1 Cause the last one is going to be the BE or Default queue...

                                                        {
      EV << "ONUMacCtl: MPCP Frame processing\n";
      MPCP * mpcp = check_and_cast<MPCP *>(frame);


      switch (mpcp->getOpcode())
      {
            case MPCP_REGISTER:
            {

                  MPCPRegAck *ack = new MPCPRegAck();
                  MPCPRegister * reg = check_and_cast<MPCPRegister *>(frame);

                  EV << "ONUMacCtl: Type is MPCP_REGISTER\n";
                  cancelEvent(regTOMsg);
                  EV << "ONUMacCtl: Canceling RegTOMsg\n";

                  ack->setOpcode(MPCP_REGACK);
                  ack->setName("MPCPAck");
                  ack->setEtherType(MPCP_TYPE);
                  ack->setDest(mpcp->getSrc());
                  ack->setByteLength(MPCP_HEADER_LEN);

                  int LLID_num=reg->getPtpNumReg();

                  EV<< "Assigned LLIDs:" <<LLID_num<<endl;
                  for (uint8_t i=0; i<reg->getLLIDsArraySize(); i++){
                        // 0xFFF = 4095 intrand -> [0-4095)
                        EV<< (int)i<<"  " <<(int)reg->getLLIDs(i)<<endl;
                        QueuePerLLid tmp_qpllid;
                        tmp_qpllid.llid = reg->getLLIDs(i);
                        std::string tmp_name="Default";
                        tmp_qpllid.prior = 0.0;
                        if (serviceList && serviceList->size() > i) {
                              tmp_name=serviceList->at(i).name;
                              EV<<tmp_name<<" -- "<<reg->getLLIDs(i)<<endl;
                              tmp_qpllid.prior = serviceList->at(i).priority;
                              tmp_qpllid.isDefault = false;
                        }

                        // Use the last LLiD as the default
                        if ( reg->getLLIDsArraySize() -1 == i){
                              EV << "Setting the default LLiD (BE) to "<<tmp_qpllid.llid<<endl;
                              tmp_name="Default";
                              tmp_qpllid.prior = 0.0001;
                              tmp_qpllid.isDefault = true;
                        }

                        tmp_qpllid.setServiceName(tmp_name);
                        tmp_qpllid.queueLimit = queueLimit;
                        tmp_qpllid.vec->setGranularity(granularity);
                        addSortedLLID(tmp_qpllid);

                  }

                  //send(ack,"lowerLayerOut");
                  // Urb@n: Send directly on upstream to avoid collisions
                  ack->addByteLength(PREAMBLE_BYTES);
                  ack->setSrc(opt_mac);
                  sendDirect(ack, olt_mac, olt_mac->gate("direct")->getId());

                  // Send the frame on top layer that manages LLIDs
                  send(frame->dup(),"upperLayerOut");

                  break;
            }
            case MPCP_GATE:
            {
                  MPCPGate * gate = check_and_cast<MPCPGate *>(frame);
                  EV << "ONUQPerLLiDBase: Type is MPCP_GATE\n";


                  // Register Grant
                  if (gate->getListLen() == 1
                        && gate->getDest().isBroadcast())
                  {
                        EV << "ONUMacCtl: MPCP REGISTER GRANT (DOOUUU)" <<endl;
                        // Process here number of llids...
                        if (serviceList) numOfLLIDs = serviceList->size()+1;
                        startMPCPReg(gate->getDuration(0));
                        break;
                  }

                  break;
            }
            default:
                  EV << "ONUMacCtl: Unrecognized MPCP OpCode!!\n";
                  return;
      };

      delete frame;
}

Return an MPCP REPORT with the status of the queues.

                                                {
      MPCPReport * rep = new MPCPReport();
      rep->setOpcode(MPCP_REPORT);
      rep->setEtherType(MPCP_TYPE);
      // TODO: replace with OLT mac... at some point
      rep->setDest(MACAddress("FF:FF:FF:FF:FF:FF"));
      rep->setSrc(opt_mac);

      rep->setQInfoArraySize(pon_queues.size());

      uint8_t bitFlag=0;
      // For Each Queue
      for (uint i=0; i<pon_queues.size(); i++){
            // Enable the proper bit
            bitFlag|=(0x01<<(7-i));
            EV<<"BF: "<<bitFlag<<endl;

            // Convert bits in the queue to ns16 request
            // TODO: hardcoded line rate 1Gpbs
            uint32_t tmpreq=MPCPTools::bitsToNS16(pon_queues[i].getBitLength(), 1);

            // Set the requested bandwidth...
            rep->setQInfo(i, tmpreq);
      }

      // Set the Map
      rep->setBitMap(bitFlag);

      // Header + bitMap + #*uint16 (uint32 is used to avoid overflows...)
      rep->setByteLength(getMPCPRepSize());

      // TODO: the std say that it should be mapped to an LLID
      // use EtherFrameWithLLID...
      return rep;
}
virtual void ONUQPerLLiDBase::requestPacket ( ) [pure virtual]

The queue should send a packet whenever this method is invoked.

If the queue is currently empty, it should send a packet when when one becomes available.

Implemented in ONU_QPL_RR.

void ONUQPerLLiDBase::sendMPCPReg ( ) [protected, virtual]
                                 {



      MPCPRegReq *regreq = new MPCPRegReq();
      regreq->setDest(MACAddress("FF:FF:FF:FF:FF:FF"));
      regreq->setEtherType(MPCP_TYPE);
      regreq->setName("MPCPRegReq");
      regreq->setOpcode(MPCP_REGREQ);
      regreq->setPtpNumReq(numOfLLIDs);

      regreq->setByteLength(MPCP_HEADER_LEN+MPCP_LIST_LEN);

      // Send REG REQ and Schedule a timeout
      //send(regreq, "lowerLayerOut");

      // Urb@n: Send directly on upstream to avoid collisions
      regreq->addByteLength(PREAMBLE_BYTES);
      regreq->setSrc(opt_mac);
      sendDirect(regreq, olt_mac, olt_mac->gate("direct")->getId());

      scheduleAt(simTime() +regTimeOut, regTOMsg );
}
void ONUQPerLLiDBase::startMPCPReg ( uint32_t  regMaxRandomSleep) [protected, virtual]

Initializes the MPCP registration process and sets the regTOMsg (TimeOut for the registration)

                                                            {

      uint32_t rndBackOff = dblrand() * regMaxRandomSleep;
      EV<<"Sending MPCP REGREQ in " << rndBackOff << "(< " <<regMaxRandomSleep<<")"<< endl;
      simtime_t t;
      t.setRaw(simTime().raw() + MPCPTools::ns16ToSimTime(rndBackOff));
      scheduleAt(t, regSendMsg );
}

Member Data Documentation

bool ONUQPerLLiDBase::allQsEmpty [protected]
double ONUQPerLLiDBase::granularity [protected]

Granularity of QoS limit (i.e. per second <- most common)

int ONUQPerLLiDBase::nextQIndex [protected]
int ONUQPerLLiDBase::numOfLLIDs [protected]
cModule* ONUQPerLLiDBase::olt_mac [protected]

The OLT MAC module for direct delivery.

MACAddress ONUQPerLLiDBase::opt_mac [protected]

The Current ONU Optical MAC Address.

int ONUQPerLLiDBase::queueLimit [protected]
cMessage * ONUQPerLLiDBase::regSendMsg [protected]
double ONUQPerLLiDBase::regTimeOut [protected]
cMessage* ONUQPerLLiDBase::regTOMsg [protected]

The documentation for this class was generated from the following files:
 All Classes Files Functions Variables Typedefs Friends Defines