EPONImplementationforOMNet++
0.8Beta
|
OLT_Q_mgmt_PerLLiD creates on queue per MAC-LLID tuple. More...
#include <OLTQPerLLiDBase.h>
Public Member Functions | |
OLTQPerLLiDBase () | |
virtual | ~OLTQPerLLiDBase () |
Protected Types | |
typedef std::vector < QueuePerMacLLid > | PonQueues |
The vector holding the queues. | |
Protected Member Functions | |
virtual void | initialize () |
virtual void | finish () |
virtual void | handleMessage (cMessage *msg) |
virtual void | processFrameFromHigherLayer (cMessage *msg) |
virtual void | processFrameFromLowerLayer (cMessage *msg) |
virtual void | processMPCP (EthernetIIFrame *frame) |
Do MPCP stuff. | |
virtual void | handleRegTimeOut (cMessage *msg) |
virtual void | handleMPCPReport (MPCPReport *msg) |
virtual void | doOnuRegistration (MACAddress mac) |
virtual cModule * | findModuleUp (const char *name) |
virtual void | createONU_Q (ONUTableEntry &en) |
Create a new queue for an entry in the ONU table. | |
virtual bool | existsInPONQueues (mac_llid ml) |
virtual void | DoUpstreamDBA () |
This method calculates the upstream timers for each ONU in the ONUTable. | |
virtual void | sendMPCPGateReg () |
Generate and send the initial gate message. | |
virtual void | SendGateUpdates () |
Generate MPCPGate messages announcing the transmission times to the ONUs. | |
virtual void | checkIfAllEmpty () |
Check is all the Qs are empty. | |
int | getIndexForLLID (uint16_t llid) |
void | addSortedMacLLID (QueuePerMacLLid tmp_qpml) |
Add a new queue to the pon_queues sorted by priority Higher priority Qs go up. | |
QueuePerMacLLid * | getFastestQForMac (const MACAddress &mac) |
Get the higher priority queue for the specified MAC address. | |
QueuePerMacLLid * | getFastestQForMac (const std::string &mac) |
Get the higher priority queue for the specified MAC address. | |
uint16_t | getFastestLLiDForMac (const std::string &mac) |
virtual bool | isEmpty () |
uint64_t | getSuperSlot_ns () |
Return the Super Slot length (ns) | |
double | getSuperSlot_sec () |
Return the Super Slot length (sec). | |
Protected Attributes | |
ONUTable * | onutbl |
vector< ONUTableEntry > | temptbl |
vector< cMessage * > | pendingAck |
SrvList * | serviceList |
double | regAckTimeOut |
uint16_t | slotLength |
uint16_t | slotNumber |
int32_t | regTimeInt |
int | queueLimit |
uint64_t | datarateLimit |
PonQueues | pon_queues |
bool | allQsEmpty |
int | nextQIndex |
OLT_Q_mgmt_PerLLiD creates on queue per MAC-LLID tuple.
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.
typedef std::vector<QueuePerMacLLid> OLTQPerLLiDBase::PonQueues [protected] |
The vector holding the queues.
OLTQPerLLiDBase::~OLTQPerLLiDBase | ( | ) | [virtual] |
{ for (uint32_t i=0; i<pendingAck.size(); i++){ cancelAndDelete((cMessage *)pendingAck[i]); } for (uint32_t i=0; i<pon_queues.size(); i++){ pon_queues[i].clean(); } pon_queues.clear(); }
void OLTQPerLLiDBase::addSortedMacLLID | ( | QueuePerMacLLid | tmp_qpml | ) | [protected] |
Add a new queue to the pon_queues sorted by priority Higher priority Qs go up.
{ // Just add it if empty... if (pon_queues.empty()) { pon_queues.push_back(tmp_qpml); return; } // Add in the proper position for (PonQueues::iterator it = pon_queues.begin(); it != pon_queues.end(); it++){ if ((*it).prior < tmp_qpml.prior){ pon_queues.insert(it, tmp_qpml); return; } } // If not added till here... add to the end pon_queues.push_back(tmp_qpml); }
void OLTQPerLLiDBase::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; for (uint32_t i=0; i< pon_queues.size(); i++){ if (!pon_queues[i].isEmpty()){ allQsEmpty = false; return; } } allQsEmpty = true; }
void OLTQPerLLiDBase::createONU_Q | ( | ONUTableEntry & | en | ) | [protected, virtual] |
Create a new queue for an entry in the ONU table.
This method also calls the DoUpstreamDBA() to calculate the new granted times and finally calls the SendGateUpdates() to announce these to the ONUs.
{ mac_llid tmp_ml; tmp_ml.mac = en.getId(); // Scan llids for (int j=0; j<en.getLLIDsNum(); j++){ tmp_ml.llid = en.getLLID(j); QueuePerMacLLid tmp_q; tmp_q.ml = tmp_ml; char tmp_name[20]; sprintf(tmp_name,"Default-%d",tmp_ml.llid); tmp_q.prior = 0.0; if (serviceList && (int)serviceList->size() > j) { sprintf(tmp_name,"%s-%d",serviceList->at(j).name.c_str(),tmp_ml.llid); tmp_q.prior = serviceList->at(j).priority; tmp_q.isDefault = false; } // Use the last LLiD as the default if ( en.getLLIDsNum() -1 == j){ EV << "Registering the default LLiD (BE) to "<<tmp_ml.llid<<endl; sprintf(tmp_name,"%s-%d","Default",tmp_ml.llid); tmp_q.prior = 0.0001; tmp_q.isDefault = true; } tmp_q.setServiceName(tmp_name); addSortedMacLLID(tmp_q); } // Do Upstream DBA and calculate tx timers DoUpstreamDBA(); // Announce these timers to the ONUs SendGateUpdates(); }
void OLTQPerLLiDBase::doOnuRegistration | ( | MACAddress | mac | ) | [protected, virtual] |
{ // Find Entry Index.... for (uint32_t i=0; i<temptbl.size(); i++){ if (temptbl.at(i).getId() == mac){ EV << "OLTMacCtl: ONU (MAC: "<<mac<<") request "<< i << " Registered"<<endl; // Clear message and move tb entry to the global cancelAndDelete(pendingAck[i]); pendingAck.erase(pendingAck.begin()+i); // Copy ONUTableEntry en=temptbl.at(i); onutbl->addONU(en); // Remove from tmp //temptbl.removeONU(i); temptbl.erase(temptbl.begin()+i); createONU_Q(en); break; } } }
void OLTQPerLLiDBase::DoUpstreamDBA | ( | ) | [protected, virtual] |
This method calculates the upstream timers for each ONU in the ONUTable.
Fair Allocation per MAC-LLID with LIMIT.
This could be overridden to change the default behavior. The default is fair allocation per ONU.
In addition this method scale down the simulation data rates. If datarateLimit is set > 0 then only this is going to be allocated to the ONUs.
FOR NON-POLLING ALGORITHMS 2 Basic RULES At the end this method should:
FOR POLLING ALGORITHMS this method should:
Reimplemented in OLTQPerLLiDBase_P.
{ EV <<"OLTQPerLLiDBase::DoUpstreamDBA"<<endl; /* * We do not need to allocate for the broadcast * Q on the upstream... so -1. Downstream is * different... */ int numOfMACsLLIDs = pon_queues.size()-1; // Check IDLE if (numOfMACsLLIDs == 0) return; double each_llid = 0; if (datarateLimit <= 0){ // This was the old one, divided all time to num of MACs/LLiDs double slots_per_ml = (double) slotNumber/numOfMACsLLIDs; each_llid = slots_per_ml*slotLength/16; } else{ // NEW!! double superSlotNS = slotNumber*slotLength; // SuperSlot Length in ns double dataratePerSS = ((double)datarateLimit * superSlotNS ) / pow(10,9); // bps (NOT Mbps) each_llid = ceil(dataratePerSS/(numOfMACsLLIDs*16)); // ns -> ns16 } /* * NOTE: CAUTION: * - ONUz _should_ handle it correctly (register shift also) * - CODE FOR REGISTER SHIFT IS IN THE ONUz SO DO NOT CALCULATE IT HERE... */ uint32_t curpointer=regTimeInt; // For each ONU generate start and length values for (int i=0; i<onutbl->getTableSize(); i++){ CommitedTime ct; ct.length=each_llid*onutbl->getEntry(i)->getLLIDsNum(); ct.start=curpointer; onutbl->getEntry(i)->setComTime(ct); curpointer+=each_llid*onutbl->getEntry(i)->getLLIDsNum(); } }
bool OLTQPerLLiDBase::existsInPONQueues | ( | mac_llid | ml | ) | [protected, virtual] |
{ // MY FIND... bool found=false; for(PonQueues::const_iterator it = pon_queues.begin(); it != pon_queues.end(); ++it) { if ((mac_llid)it->ml == ml) { found=true; break; } } return found; }
cModule * OLTQPerLLiDBase::findModuleUp | ( | const char * | name | ) | [protected, virtual] |
{ cModule *mod = NULL; for (cModule *curmod=this; !mod && curmod; curmod=curmod->getParentModule()) mod = curmod->getSubmodule(name); return mod; }
void OLTQPerLLiDBase::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"; recordScalar(name.c_str(), pon_queues[i].vec->numBytesDropped/t); name=srvName +" bytes sent/sec"; recordScalar(name.c_str(), pon_queues[i].vec->numBytesSent/t); } } // Check and Log datarateLimit if (datarateLimit == 0) datarateLimit = 1000 * pow(10,6); recordScalar("DataRateLimit", datarateLimit); }
uint16_t OLTQPerLLiDBase::getFastestLLiDForMac | ( | const std::string & | mac | ) | [protected] |
{ for (uint32_t i=0; i<pon_queues.size(); i++){ if (pon_queues[i].ml.mac.str() == mac) return pon_queues[i].ml.llid; } return 0; }
QueuePerMacLLid * OLTQPerLLiDBase::getFastestQForMac | ( | const MACAddress & | mac | ) | [protected] |
Get the higher priority queue for the specified MAC address.
Since Qs are sorted, the first match is returned
{ for (uint32_t i=0; i<pon_queues.size(); i++){ if (pon_queues[i].ml.mac.compareTo(mac) == 0) return &pon_queues[i]; } return NULL; }
QueuePerMacLLid * OLTQPerLLiDBase::getFastestQForMac | ( | const std::string & | mac | ) | [protected] |
Get the higher priority queue for the specified MAC address.
Since Qs are sorted, the first match is returned
{ for (uint32_t i=0; i<pon_queues.size(); i++){ if (pon_queues[i].ml.mac.str() == mac) return &pon_queues[i]; } return NULL; }
int OLTQPerLLiDBase::getIndexForLLID | ( | uint16_t | llid | ) | [protected] |
{ for (uint32_t i=0; i<pon_queues.size(); i++) if (pon_queues[i].ml.llid == llid) return i; return -1; }
uint64_t OLTQPerLLiDBase::getSuperSlot_ns | ( | ) | [protected] |
Return the Super Slot length (ns)
{ return slotLength*slotNumber; }
double OLTQPerLLiDBase::getSuperSlot_sec | ( | ) | [protected] |
Return the Super Slot length (sec).
Useful for calculating data rates per second.
{ return ((double)getSuperSlot_ns())/pow(10,9); }
void OLTQPerLLiDBase::handleMessage | ( | cMessage * | msg | ) | [protected, virtual] |
Reimplemented in OLTQPerLLiDBase_P.
{ // Self Message if (msg->isSelfMessage()) { EV << "Self-message " << msg << " received\n"; if (msg->getKind() == ONUPENACK) handleRegTimeOut(msg); else EV << "UnKnown Self Message\n"; 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()){ processFrameFromHigherLayer(msg); }else{ EV << "OLTMacCtl: Message came FROM THE WRONG DIRRECTION???? Dropping\n"; delete msg; } }
void OLTQPerLLiDBase::handleMPCPReport | ( | MPCPReport * | msg | ) | [protected, virtual] |
{ // Get the bit map... altho in simulation we do not // need it cause we can have the length from the lists //uint8_t bitMap = msg->getBitMap(); // Get entry ONUTableEntry * en = onutbl->getEntry(msg->getSrc()); uint64_t totalreq=0; for (uint i=0; i<msg->getQInfoArraySize(); i++){ // No need to convert here... FIXME: hardcoded line rate 1G //en->req[i] = MPCPTools::ns16ToBits(msg->getQInfo(i), 1); en->req[i] = msg->getQInfo(i); totalreq+=en->req[i]; } en->totalReq = totalreq; }
void OLTQPerLLiDBase::handleRegTimeOut | ( | cMessage * | msg | ) | [protected, virtual] |
{ // Find Message Index.... for (uint32_t i=0; i<pendingAck.size(); i++){ if (pendingAck[i] == msg){ EV << "OLTMacCtl: ONU request "<< i << " TimeOut"<<endl; // Clear message and tb entry cancelAndDelete((cMessage *)pendingAck[i]); pendingAck.erase(pendingAck.begin()+i); //temptbl.removeONU(i); temptbl.erase(temptbl.begin()+i); } } }
void OLTQPerLLiDBase::initialize | ( | ) | [protected, virtual] |
Reimplemented in OLTQPerLLiDBase_P, and OLT_QPL_RR.
{ // ONU table onutbl = NULL; onutbl = dynamic_cast<ONUTable *>( findModuleUp("onuTable")); if (!onutbl) error("Shit... no ONU table found ?!?!"); // Service List serviceList=NULL; if (dynamic_cast<ServiceConfig *>( findModuleUp("serviceConfig")) != NULL){ serviceList = &(dynamic_cast<ServiceConfig *>( findModuleUp("serviceConfig"))->srvs); } // Parameters regAckTimeOut = par("regAckTimeOut"); regAckTimeOut/=1000; // in ns slotLength=par("slotLength"); slotNumber=par("slotNumber"); // Convert to ns16 time // (to be added in the MPCP frames) regTimeInt = par("regTimeInt"); regTimeInt*=(double)1000 / 16; queueLimit = par("queueLimit"); // Create Q for BroadCasts QueuePerMacLLid tmp_qpml; tmp_qpml.setServiceName("BroadCast"); tmp_qpml.prior = 0.0; tmp_qpml.ml.llid = LLID_EPON_BC; addSortedMacLLID(tmp_qpml); allQsEmpty=true; nextQIndex = 0; datarateLimit=(uint64_t)(par("datarateLimit").doubleValue()*pow(10,6)); if (datarateLimit>pow(10,10)) error("Ensure that datarateLimit parameter is not negative..."); // WATCH WATCH_VECTOR(pon_queues); WATCH(allQsEmpty); sendMPCPGateReg(); }
bool OLTQPerLLiDBase::isEmpty | ( | ) | [protected, virtual] |
{ checkIfAllEmpty(); return allQsEmpty; }
void OLTQPerLLiDBase::processFrameFromHigherLayer | ( | cMessage * | msg | ) | [protected, virtual] |
{ EV << "OLT_Q_mgmt_PerLLiD: Incoming from higher layer...\n"; /* * Notify the lower layer. */ if (allQsEmpty){ EV << "Direct tx: "<<msg<<endl; send(msg, "lowerLayerOut"); return; } 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 || nfo->llid == LLID_EPON_BC){ Q_index = getIndexForLLID(LLID_EPON_BC); }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(); // Check that 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(); delete msg; return; }else{ // Record the incoming bytes pon_queues[Q_index].vec->recordVectors(); } // Add it pon_queues[Q_index].insert(dynamic_cast<cPacket *>(msg)); }
void OLTQPerLLiDBase::processFrameFromLowerLayer | ( | cMessage * | msg | ) | [protected, virtual] |
{ EV << "OLT_Q_mgmt_PerLLiD: Incoming from lower layer\n"; EthernetIIFrame * frame = dynamic_cast<EthernetIIFrame *>(msg); if (frame && frame->getEtherType() == MPCP_TYPE){ processMPCP(frame); return; } // Not Control send(msg,"upperLayerOut"); }
void OLTQPerLLiDBase::processMPCP | ( | EthernetIIFrame * | frame | ) | [protected, virtual] |
Do MPCP stuff.
This handles the ONU registration and REPORT messages
{ EV << "OLTMacCtl: MPCP Frame processing\n"; MPCP * mpcp = check_and_cast<MPCP *>(frame); switch (mpcp->getOpcode()) { case MPCP_REGREQ: { MPCPRegReq * req = check_and_cast<MPCPRegReq *>(frame); MPCPRegister *rspframe = new MPCPRegister(); rspframe->setOpcode(MPCP_REGISTER); rspframe->setName("MPCPRegister"); rspframe->setEtherType(MPCP_TYPE); rspframe->setDest(req->getSrc()); rspframe->setPtpNumReg(req->getPtpNumReq()); EV << "Log the LLIDs temporarly and add a TO timer\n"; ONUTableEntry te; te.setId(req->getSrc()); // Parse and set LLIDS rspframe->setLLIDsArraySize(req->getPtpNumReq()); for (int i=0; i<req->getPtpNumReq(); i++){ // 0xFFF = 4095 intrand -> [0-4095) uint32_t llid_tmp=(uint32_t)intrand(4095); while (te.addLLID(llid_tmp) <0 ){ EV << "*** RANDOM GEN FAILED!!!!???\n"; llid_tmp=(uint32_t)intrand(4095); } rspframe->setLLIDs(i,llid_tmp); } rspframe->setByteLength(MPCP_HEADER_LEN+MPCP_LIST_LEN+req->getPtpNumReq()*MPCP_LLID_LEN); // TODO: add TimeStamp! temptbl.push_back(te); // NOTE: do NOT send... enqueue //send(rspframe,"lowerLayerOut"); processFrameFromHigherLayer(rspframe); // Create and add TO self message cMessage *tmpmsg=new cMessage("OnuTOMsg", ONUPENACK); pendingAck.push_back(tmpmsg); scheduleAt(simTime()+regAckTimeOut, tmpmsg); break; } case MPCP_REGACK: { doOnuRegistration(mpcp->getSrc()); break; } case MPCP_REPORT: { MPCPReport * rep = dynamic_cast<MPCPReport *>(frame); handleMPCPReport(rep); break; } default: EV << "Unrecognized MPCP OpCode"; }; delete frame; }
void OLTQPerLLiDBase::SendGateUpdates | ( | ) | [protected, virtual] |
Generate MPCPGate messages announcing the transmission times to the ONUs.
(For all the ONUs in the ONUTable)
This method should be called at the end of DoUpstreamDBA ONLY is the algorithm is NON-POLLING (burst) BASED. Polling algorithms have to override the DoUpstreamDBA to handle both allocation and GATE/GRANDs...
Reimplemented in OLTQPerLLiDBase_P.
{ for (int i=onutbl->getTableSize()-1; i>=0; i--){ MPCPGate *gt = new MPCPGate(); gt->setName("MPCPGate"); gt->setEtherType(MPCP_TYPE); gt->setOpcode(MPCP_GATE); MPCPTools::setGateLen(*gt, 1); gt->setSlotTime(slotLength); gt->setSlotsNum(slotNumber); gt->setDest(onutbl->getEntry(i)->getId()); gt->setStartTime(0, onutbl->getEntry(i)->getComTime().start); gt->setDuration(0, onutbl->getEntry(i)->getComTime().length); // Header + List + (start + Len) + slotNum + slotLen gt->setByteLength(MPCP_HEADER_LEN+MPCP_LIST_LEN+MPCP_TIMERS_LEN+MPCP_SLOTINFO_LEN); // Send directly send(gt, "lowerLayerOut"); } }
void OLTQPerLLiDBase::sendMPCPGateReg | ( | ) | [protected, virtual] |
Generate and send the initial gate message.
This message is used for the registration of the ONUs.
{ MPCPGate *gt = new MPCPGate(); gt->setEtherType(MPCP_TYPE); gt->setOpcode(MPCP_GATE); gt->setName("MPCPGate(Reg)"); MPCPTools::setGateLen(*gt, 1); gt->setDuration(0, regTimeInt); gt->setDest(MACAddress::BROADCAST_ADDRESS); gt->setSlotTime(slotLength); gt->setSlotsNum(slotNumber); // Header + List + (start + Len) + slotNum + slotLen gt->setByteLength(MPCP_HEADER_LEN+MPCP_LIST_LEN+MPCP_TIMERS_LEN+MPCP_SLOTINFO_LEN); send(gt, "lowerLayerOut"); }
bool OLTQPerLLiDBase::allQsEmpty [protected] |
uint64_t OLTQPerLLiDBase::datarateLimit [protected] |
int OLTQPerLLiDBase::nextQIndex [protected] |
ONUTable* OLTQPerLLiDBase::onutbl [protected] |
vector<cMessage *> OLTQPerLLiDBase::pendingAck [protected] |
PonQueues OLTQPerLLiDBase::pon_queues [protected] |
int OLTQPerLLiDBase::queueLimit [protected] |
double OLTQPerLLiDBase::regAckTimeOut [protected] |
int32_t OLTQPerLLiDBase::regTimeInt [protected] |
SrvList* OLTQPerLLiDBase::serviceList [protected] |
uint16_t OLTQPerLLiDBase::slotLength [protected] |
uint16_t OLTQPerLLiDBase::slotNumber [protected] |
vector<ONUTableEntry> OLTQPerLLiDBase::temptbl [protected] |