Quon.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2006 Institut fuer Telematik, Universitaet Karlsruhe (TH)
00003 //
00004 // This program is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU General Public License
00006 // as published by the Free Software Foundation; either version 2
00007 // of the License, or (at your option) any later version.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with this program; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 //
00018 
00025 #include <Quon.h>
00026 #include <BootstrapList.h>
00027 #include <limits>
00028 
00029 Define_Module(Quon);
00030 
00031 void Quon::initializeOverlay(int stage)
00032 {
00033     // because of IPAddressResolver, we need to wait until interfaces are registered,
00034     // address auto-assignment takes place etc.
00035     if(stage != MIN_STAGE_OVERLAY) {
00036         return;
00037     }
00038 
00039     // fetch parameters
00040     minAOI = (double)par("minAOIWidth") * (double)par("AOIBufferFactor"); // FIXME: use buffer only where required
00041     maxAOI = (double)par("AOIWidth") * (double)par("AOIBufferFactor"); // FIXME: use buffer only where required
00042     AOIWidth = maxAOI;
00043     connectionLimit = par("connectionLimit");
00044     areaDimension = par("areaDimension");
00045     joinTimeout = par("joinTimeout");
00046     deleteTimeout = par("deleteTimeout");
00047     aliveTimeout = (double)par("aliveTimeout") / 2.0;
00048     backupIntervall = par("contactBackupIntervall");
00049     AOIAdaptionIntervall = par("AOIAdaptionIntervall");
00050     linearAdaption = par("AOIAdaptLinear");
00051     adaptionSensitivity = par("AOIAdaptionSensitivity");
00052     gossipSensitivity = par("AOIGossipSensitivity");
00053     useSquareMetric = par("useSquareMetric");
00054 
00055     // determine wether we want dynamic AOI or not
00056     useDynamicAOI = (connectionLimit > 0 && minAOI < maxAOI && AOIAdaptionIntervall > 0.0) ? true : false;
00057 
00058     // set node key and thisSite pointer
00059     thisNode.setKey(OverlayKey::random());
00060     thisSite = new QuonSite();
00061     thisSite->address = thisNode;
00062     thisSite->type = QTHIS;
00063 
00064     // initialize self-messages
00065     join_timer = new cMessage("join_timer");
00066     sec_timer = new cMessage("sec_timer");
00067     alive_timer = new cMessage("alive_timer");
00068     backup_timer = new cMessage("backup_timer");
00069     adaption_timer = new cMessage("adaption_timer");
00070 
00071     // statistics
00072     joinRequestBytesSend = 0.0;
00073     joinAcknowledgeBytesSend = 0.0;
00074     nodeMoveBytesSend = 0.0;
00075     newNeighborsBytesSend = 0.0;
00076     nodeLeaveBytesSend = 0.0;
00077     maxBytesPerSecondSend = 0.0;
00078     averageBytesPerSecondSend = 0.0;
00079     bytesPerSecond = 0.0;
00080     softConnections = 0;
00081     softNeighborCount = 0;
00082     bindingNeighborCount = 0;
00083     directNeighborCount = 0;
00084     secTimerCount = 0;
00085     //rejoinCount = 0;
00086     avgAOI= 0 ;
00087 
00088     // watch some variables
00089     WATCH(thisSite->address);
00090     WATCH(thisSite->position);
00091     WATCH(AOIWidth);
00092     //WATCH_POINTER_MAP(Sites);
00093     //WATCH_POINTER_MAP(deletedSites);
00094     //WATCH_SET(Positions);
00095     WATCH(joinRequestBytesSend);
00096     WATCH(joinAcknowledgeBytesSend);
00097     WATCH(nodeMoveBytesSend);
00098     WATCH(newNeighborsBytesSend);
00099     WATCH(nodeLeaveBytesSend);
00100     WATCH(maxBytesPerSecondSend);
00101     WATCH(bytesPerSecond);
00102     WATCH(softConnections);
00103     WATCH(softNeighborCount);
00104     WATCH(bindingNeighborCount);
00105     WATCH(directNeighborCount);
00106     //WATCH(rejoinCount);
00107 
00108     // set initial state
00109     changeState(QUNINITIALIZED);
00110     changeState(QJOINING);
00111 }
00112 
00113 void Quon::changeState(QState qstate)
00114 {
00115     this->qstate = qstate;
00116     switch(qstate) {
00117         case QUNINITIALIZED:
00118             globalNodeList->removePeer(thisSite->address);
00119             cancelEvent(join_timer);
00120             cancelEvent(sec_timer);
00121             cancelEvent(alive_timer);
00122             cancelEvent(backup_timer);
00123             cancelEvent(adaption_timer);
00124             break;
00125         case QJOINING:
00126             scheduleAt(simTime(), join_timer);
00127             scheduleAt(simTime() + 1.0, sec_timer);
00128             break;
00129         case QREADY:
00130             cancelEvent(join_timer);
00131             globalNodeList->registerPeer(thisSite->address);
00132             // tell the application we are ready unless we are rejoining the overlay
00133             //if(rejoinCount == 0) {
00134             CompReadyMessage* readyMsg = new CompReadyMessage("OVERLAY_READY");
00135             readyMsg->setReady(true);
00136             readyMsg->setComp(getThisCompType());
00137             // TODO/FIXME: use overlay->sendMessageToAllComp(msg, getThisCompType())?
00138             sendToApp(readyMsg);
00139             //}
00140             // set initial AOI size
00141             AOIWidth = maxAOI;
00142             GameAPIResizeAOIMessage* gameMsg = new GameAPIResizeAOIMessage("RESIZE_AOI");
00143             gameMsg->setCommand(RESIZE_AOI);
00144             gameMsg->setAOIsize(AOIWidth);
00145             sendToApp(gameMsg);
00146             if(aliveTimeout > 0.0) {
00147                 scheduleAt(simTime() + aliveTimeout, alive_timer);
00148             }
00149             if(backupIntervall > 0.0) {
00150                 scheduleAt(simTime() + backupIntervall, backup_timer);
00151             }
00152             if(useDynamicAOI) {
00153                 scheduleAt(simTime() + AOIAdaptionIntervall, adaption_timer);
00154             }
00155             break;
00156     }
00157     setBootstrapedIcon();
00158     // debug output
00159     if(debugOutput) {
00160         EV << "[Quon::changeState() @ " << thisNode.getAddress()
00161            << " (" << thisNode.getKey().toString(16) << ")]\n"
00162            << "    Node " << thisSite->address.getAddress() << " entered ";
00163         switch(qstate) {
00164             case QUNINITIALIZED:
00165                 EV << "UNINITIALIZED";
00166                 break;
00167             case QJOINING:
00168                 EV << "JOINING";
00169                 break;
00170             case QREADY:
00171                 EV << "READY";
00172                 break;
00173         }
00174         EV << " state." << endl;
00175     }
00176 }
00177 
00178 void Quon::handleTimerEvent(cMessage* msg)
00179 {
00180     if(msg->isName("join_timer")) {
00181         //reset timer
00182         cancelEvent(join_timer);
00183         if(qstate != QREADY) {
00184             scheduleAt(simTime() + joinTimeout, msg);
00185             // handle event
00186             processJoinTimer();
00187         }
00188     }
00189     else if(msg->isName("sec_timer")) {
00190         //reset timer
00191         cancelEvent(sec_timer);
00192         scheduleAt(simTime() + 1, msg);
00193         // handle event
00194         processSecTimer();
00195     }
00196     else if(msg->isName("delete_timer")) {
00197         // handle event
00198         processDeleteTimer(msg);
00199     }
00200     else if(msg->isName("alive_timer")) {
00201         //reset timer
00202         cancelEvent(alive_timer);
00203         scheduleAt(simTime() + aliveTimeout, msg);
00204         // handle event
00205         processAliveTimer();
00206     }
00207     else if(msg->isName("backup_timer")) {
00208         //reset timer
00209         cancelEvent(backup_timer);
00210         scheduleAt(simTime() + backupIntervall, msg);
00211         // handle event
00212         processBackupTimer();
00213     }
00214     else if(msg->isName("adaption_timer")) {
00215         //reset timer
00216         cancelEvent(adaption_timer);
00217         scheduleAt(simTime() + AOIAdaptionIntervall, msg);
00218         // adjust AOIWidth
00219 #if 0
00220         // Old simple adaption
00221         if((Sites.size() > connectionLimit && AOIWidth > minAOI) || (Sites.size() < connectionLimit && AOIWidth < maxAOI)) {
00222             AOIWidth -= (maxAOI - minAOI) * (Sites.size() - connectionLimit) / 200.0;
00223             if(AOIWidth > maxAOI) {
00224                 AOIWidth = maxAOI;
00225             }
00226             else if(AOIWidth < minAOI) {
00227                 AOIWidth = minAOI;
00228             }
00229             GameAPIResizeAOIMessage* gameMsg = new GameAPIResizeAOIMessage("RESIZE_AOI");
00230             gameMsg->setCommand(RESIZE_AOI);
00231             gameMsg->setAOIsize(AOIWidth);
00232             sendToApp(gameMsg);
00233         }
00234 
00235 #endif
00236         double oldAOI = AOIWidth;
00237         if( linearAdaption ) {
00238             AOIWidth -= (maxAOI - minAOI) * ((double) Sites.size() - (double) connectionLimit) * adaptionSensitivity / (double) connectionLimit;
00239         } else if( Sites.size() > 0 ){
00240             AOIWidth *= (1-adaptionSensitivity) + (double) connectionLimit * adaptionSensitivity / (double) Sites.size();
00241         }
00242         if( gossipSensitivity > 0  && Sites.size() > 0 ) {
00243             double avgNeighborAOI = 0;
00244             for( QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites ){
00245                 avgNeighborAOI += itSites->second->AOIwidth;
00246             }
00247             avgNeighborAOI /= Sites.size();
00248             AOIWidth = AOIWidth*(1-gossipSensitivity) + avgNeighborAOI*gossipSensitivity;
00249         }
00250         if(AOIWidth > maxAOI) {
00251             AOIWidth = maxAOI;
00252         }
00253         else if(AOIWidth < minAOI) {
00254             AOIWidth = minAOI;
00255         }
00256 
00257         if( oldAOI != AOIWidth ){
00258             GameAPIResizeAOIMessage* gameMsg = new GameAPIResizeAOIMessage("RESIZE_AOI");
00259             gameMsg->setCommand(RESIZE_AOI);
00260             gameMsg->setAOIsize(AOIWidth);
00261             sendToApp(gameMsg);
00262         }
00263     }
00264 }
00265 
00266 void Quon::handleAppMessage(cMessage* msg)
00267 {
00268     GameAPIMessage* gameAPIMsg = dynamic_cast<GameAPIMessage*>(msg);
00269     if(gameAPIMsg != NULL) {
00270         // debug output
00271         if(debugOutput) {
00272             EV << "[Quon::handleAppMessage() @ " << thisNode.getAddress()
00273                << " (" << thisNode.getKey().toString(16) << ")]\n"
00274                << "    Node " << thisSite->address.getAddress() << " received " << gameAPIMsg->getName() << " from application."
00275                << endl;
00276         }
00277         switch(gameAPIMsg->getCommand()) {
00278             case MOVEMENT_INDICATION: {
00279                 GameAPIPositionMessage* gameAPIPositionMsg = dynamic_cast<GameAPIPositionMessage*>(msg);
00280                 if(qstate == QJOINING) {
00281                     handleJoin(gameAPIPositionMsg);
00282                 }
00283                 else if(qstate == QREADY) {
00284                     handleMove(gameAPIPositionMsg);
00285                 }
00286             } break;
00287             case GAMEEVENT_CHAT:
00288             case GAMEEVENT_SNOW:
00289             case GAMEEVENT_FROZEN: {
00290                 handleEvent(gameAPIMsg);
00291             } break;
00292         }
00293     }
00294     delete msg;
00295 }
00296 
00297 void Quon::handleUDPMessage(BaseOverlayMessage* msg)
00298 {
00299     if(qstate == QUNINITIALIZED) {
00300         delete msg;
00301         return;
00302     }
00303     QuonMessage* quonMsg = dynamic_cast<QuonMessage*>(msg);
00304     if(quonMsg != NULL) {
00305         // debug output
00306         if(debugOutput) {
00307             EV << "[Quon::handleUDPMessage() @ " << thisNode.getAddress()
00308                << " (" << thisNode.getKey().toString(16) << ")]\n"
00309                << "    Node " << thisSite->address.getAddress() << " received " << quonMsg->getName() << " from " << quonMsg->getSender().getAddress() << "."
00310                << endl;
00311         }
00312         if(qstate == QREADY) {
00313             switch(quonMsg->getCommand()) {
00314                 case JOIN_REQUEST: {
00315                     handleJoinRequest(quonMsg);
00316                 } break;
00317                 case NODE_MOVE: {
00318                     QuonMoveMessage* quonMoveMsg = dynamic_cast<QuonMoveMessage*>(msg);
00319                     handleNodeMove(quonMoveMsg);
00320                     delete msg;
00321                 } break;
00322                 case NEW_NEIGHBORS: {
00323                     QuonListMessage* quonListMsg = dynamic_cast<QuonListMessage*>(msg);
00324                     handleNewNeighbors(quonListMsg);
00325                     delete msg;
00326                 } break;
00327                 case NODE_LEAVE: {
00328                     QuonListMessage* quonListMsg = dynamic_cast<QuonListMessage*>(msg);
00329                     handleNodeLeave(quonListMsg);
00330                     delete msg;
00331                 } break;
00332                 case QUON_EVENT: {
00333                     sendToApp(quonMsg->decapsulate());
00334                     delete quonMsg;
00335                 } break;
00336             }
00337         }
00338         else if(qstate == QJOINING && quonMsg->getCommand() == JOIN_ACKNOWLEDGE) {
00339             QuonListMessage* quonListMsg = dynamic_cast<QuonListMessage*>(msg);
00340             handleJoinAcknowledge(quonListMsg);
00341             delete msg;
00342         }
00343         else {
00344             delete msg;
00345         }
00346     }
00347     else {
00348         delete msg;
00349     }
00350 }
00351 
00352 bool Quon::addSite(Vector2D p, NodeHandle node, double AOI, bool isSoft, QUpdateType update)
00353 {
00354     QuonSiteMap::iterator itSites = Sites.find(node.getKey());
00355     if(node.getKey() != thisSite->address.getKey() && deletedSites.find(node.getKey()) == deletedSites.end()) {
00356         if(itSites == Sites.end()) {
00357             if(debugOutput) {
00358                 EV << "[Quon::addSite() @ " << thisNode.getAddress()
00359                    << " (" << thisNode.getKey().toString(16) << ")]\n"
00360                    << "    Site " << node.getAddress() << " at " << p << " has been added to the list."
00361                    << endl;
00362             }
00363             QuonSite* temp = new QuonSite();
00364             temp->position = p;
00365             temp->address = node;
00366             if(update == QDIRECT) {
00367                 temp->dirty = true;
00368             }
00369             temp->alive = true;
00370             temp->type = QUNDEFINED;
00371             temp->softNeighbor = isSoft;
00372             temp->AOIwidth = AOI;
00373 
00374             Sites.insert(std::make_pair(temp->address.getKey(), temp));
00375         }
00376         else if(update == QDIRECT || !itSites->second->alive) {
00377             if(debugOutput) {
00378                 EV << "[Quon::addSite() @ " << thisNode.getAddress()
00379                    << " (" << thisNode.getKey().toString(16) << ")]\n"
00380                    << "    Site " << node.getAddress() << " at " << p << " has been updated in the list."
00381                    << endl;
00382             }
00383             itSites->second->position = p;
00384             itSites->second->dirty = true;
00385             itSites->second->alive = true;
00386             itSites->second->softNeighbor = isSoft;
00387             itSites->second->type = QUNDEFINED;
00388             itSites->second->AOIwidth = AOI;
00389         }
00390         return true;
00391     }
00392     return false;
00393 }
00394 
00395 void Quon::updateThisSite(Vector2D p)
00396 {
00397     if(debugOutput) {
00398         EV << "[Quon::updateThisSite() @ " << thisNode.getAddress()
00399            << " (" << thisNode.getKey().toString(16) << ")]\n"
00400            << "    This Site position has been updated to " << p << "."
00401            << endl;
00402     }
00403     thisSite->position = p;
00404 }
00405 
00406 void Quon::classifySites()
00407 {
00408     if(Sites.size() > 0) {
00409         QuonAOI AOI(thisSite->position, AOIWidth, useSquareMetric);
00410         QuonSite* bindings[4] = {0, 0, 0, 0};
00411         QuonSite* backupCandidates[4] = {0, 0, 0, 0};
00412         double bindingDistance[4] = {std::numeric_limits<double>::infinity(),
00413                                      std::numeric_limits<double>::infinity(),
00414                                      std::numeric_limits<double>::infinity(),
00415                                      std::numeric_limits<double>::infinity()};
00416         for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00417             QuonAOI NeighborAOI(itSites->second->position, itSites->second->AOIwidth, useSquareMetric);
00418             if(AOI.collide(itSites->second->position) || NeighborAOI.collide(thisSite->position)) {
00419                 if(itSites->second->type != QNEIGHBOR) {
00420                     itSites->second->type = QNEIGHBOR;
00421                     itSites->second->dirty = true;
00422                 }
00423             }
00424             else if(itSites->second->type != QUNDEFINED) {
00425                 itSites->second->type = QUNDEFINED;
00426                 itSites->second->dirty = true;
00427             }
00428             int quad = thisSite->position.getQuadrant( itSites->second->position );
00429             int dist;
00430             if( useSquareMetric ) {
00431                 dist = thisSite->position.xyMaxDistance(itSites->second->position);
00432             } else {
00433                 dist = thisSite->position.distanceSqr(itSites->second->position);
00434             }
00435 
00436             if( dist < bindingDistance[quad] ){
00437                backupCandidates[quad] = bindings[quad];
00438                bindings[quad] = itSites->second;
00439                bindingDistance[quad] = dist;
00440             }
00441         }
00442         for( int i = 0; i < 4; ++i ){
00443             if( bindings[i] ){
00444                 bindings[i]->type = QBINDING;
00445                 bindings[i]->dirty = true;
00446 //                if(!AOI.collide(bindings[i]->position)) {
00447 //                    bindingBackup[i] = bindings[i]->address;
00448                 if( backupCandidates[i] ){
00449                     bindingBackup[i] = backupCandidates[i]->address;
00450                 }
00451             }
00452         }
00453     }
00454     else {
00455         ++rejoinCount;
00456 
00457         changeState(QUNINITIALIZED);
00458         changeState(QJOINING);
00459     }
00460 }
00461 
00462 bool Quon::deleteSite(NodeHandle node)
00463 {
00464     QuonSiteMap::iterator itSites = Sites.find(node.getKey());
00465     if(itSites != Sites.end()) {
00466         if(debugOutput) {
00467             EV << "[Quon::deleteSite() @ " << thisNode.getAddress()
00468                << " (" << thisNode.getKey().toString(16) << ")]\n"
00469                << "    Site " << node.getAddress() << " at " << itSites->second->position << " has been removed from the list."
00470                << endl;
00471         }
00472         delete itSites->second;
00473         Sites.erase(itSites);
00474         return true;
00475     }
00476     return false;
00477 }
00478 
00479 int Quon::purgeSites(QPurgeType purgeSoftSites)
00480 {
00481     int purged = 0;
00482     QuonSiteMap::iterator itSites = Sites.begin();
00483     while(itSites != Sites.end()) {
00484         // Purge softNeighbors only if QPURGESOFT is set
00485         if(itSites->second->type == QUNDEFINED && ( purgeSoftSites == QPURGESOFT || !itSites->second->softNeighbor) ) {
00486             if(debugOutput) {
00487                 EV << "[Quon::purgeSites() @ " << thisNode.getAddress()
00488                    << " (" << thisNode.getKey().toString(16) << ")]\n"
00489                    << "    Site " << itSites->second->address.getAddress() << " at " << itSites->second->position << " has been removed from the list.\n"
00490                    << "    Status: " << ((itSites->second->type == QUNDEFINED) ? "QUNDEFINED" : "QSOFT")
00491                    << endl;
00492             }
00493             delete itSites->second;
00494             Sites.erase(itSites++);
00495             ++purged;
00496         }
00497         else {
00498             ++itSites;
00499         }
00500     }
00501     return purged;
00502 }
00503 
00504 void Quon::handleNodeGracefulLeaveNotification()
00505 {
00506     if(qstate == QREADY) {
00507         CompReadyMessage* readyMsg = new CompReadyMessage("OVERLAY_FINISHED");
00508         readyMsg->setReady(false);
00509         readyMsg->setComp(getThisCompType());
00510         // TODO/FIXME: use overlay->sendMessageToAllComp(msg, getThisCompType())?
00511         sendToApp(readyMsg);
00512         if(Sites.size() > 0) {
00513             // generate node leave messages
00514             QuonListMessage* quonListMsg = new QuonListMessage("NODE_LEAVE");
00515             quonListMsg->setCommand(NODE_LEAVE);
00516             quonListMsg->setSender(thisSite->address);
00517             quonListMsg->setPosition(thisSite->position);
00518             quonListMsg->setAOIsize(AOIWidth);
00519             // fill neighbors list
00520             quonListMsg->setNeighborHandleArraySize(Sites.size());
00521             quonListMsg->setNeighborPositionArraySize(Sites.size());
00522             int i = 0;
00523             for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00524                 quonListMsg->setNeighborHandle(i, itSites->second->address);
00525                 quonListMsg->setNeighborPosition(i, itSites->second->position);
00526                 ++i;
00527             }
00528             quonListMsg->setBitLength(QUONLIST_L(quonListMsg));
00529 
00530             for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00531                 QuonListMessage* quonCopyMsg = new QuonListMessage(*quonListMsg);
00532                 sendMessage(quonCopyMsg, itSites->second->address);
00533             }
00534             delete quonListMsg;
00535         }
00536         changeState(QUNINITIALIZED);
00537     }
00538 }
00539 
00540 void Quon::processJoinTimer()
00541 {
00542     GameAPIMessage* gameMsg = new GameAPIMessage("MOVEMENT_REQUEST");
00543     gameMsg->setCommand(MOVEMENT_REQUEST);
00544     sendToApp(gameMsg);
00545 }
00546 
00547 void Quon::processSecTimer()
00548 {
00549     RECORD_STATS(
00550         if(bytesPerSecond > maxBytesPerSecondSend) {
00551             maxBytesPerSecondSend = bytesPerSecond;
00552         }
00553         avgAOI += AOIWidth;
00554         averageBytesPerSecondSend += bytesPerSecond;
00555         for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00556             switch(itSites->second->type) {
00557                 case QNEIGHBOR:
00558                     directNeighborCount++;
00559                     break;
00560                 case QBINDING:
00561                     bindingNeighborCount++;
00562                     break;
00563                 case QUNDEFINED:
00564                     if( itSites->second->softNeighbor ){
00565                         softNeighborCount++;
00566                     }
00567                     break;
00568                 case QTHIS:
00569                     break;
00570             }
00571         }
00572         ++secTimerCount;
00573     );
00574     bytesPerSecond = 0.0;
00575 }
00576 
00577 void Quon::processDeleteTimer(cMessage* msg)
00578 {
00579     QuonSelfMessage* quonMsg = dynamic_cast<QuonSelfMessage*>(msg);
00580     QDeleteMap::iterator itSite = deletedSites.find(quonMsg->getKey());
00581     if(itSite != deletedSites.end()) {
00582         deletedSites.erase(itSite);
00583     }
00584     cancelAndDelete(quonMsg);
00585 }
00586 
00587 void Quon::processAliveTimer()
00588 {
00589     bool rebuild = false;
00590     QuonSiteMap::iterator itSites = Sites.begin();
00591     while(itSites != Sites.end()) {
00592         if(itSites->second->alive) {
00593             itSites->second->alive = false;
00594             ++itSites;
00595         }
00596         else {
00597             NodeHandle node = itSites->second->address;
00598             ++itSites;
00599             deleteSite(node);
00600             // update simple client
00601             deleteAppNeighbor(node);
00602             if(!rebuild) {
00603                 rebuild = true;
00604             }
00605         }
00606     }
00607     if(rebuild) {
00608         classifySites();
00609         // update simple client
00610         synchronizeAppNeighbors();
00611         purgeSites();
00612     }
00613 }
00614 
00615 void Quon::processBackupTimer()
00616 {
00617     QuonMoveMessage* quonMoveMsg = new QuonMoveMessage("NODE_MOVE");
00618     quonMoveMsg->setCommand(NODE_MOVE);
00619     quonMoveMsg->setSender(thisSite->address);
00620     quonMoveMsg->setPosition(thisSite->position);
00621     quonMoveMsg->setAOIsize(AOIWidth);
00622     quonMoveMsg->setNewPosition(thisSite->position);
00623     quonMoveMsg->setIsBinding(true);
00624     for(unsigned int i=0; i<4; i++) {
00625         if(!bindingBackup[i].isUnspecified()) {
00626             QuonMoveMessage* copyMsg = new QuonMoveMessage(*quonMoveMsg);
00627             copyMsg->setBitLength(QUONMOVE_L(copyMsg));
00628             sendMessage(copyMsg, bindingBackup[i]);
00629         }
00630     }
00631     delete quonMoveMsg;
00632 }
00633 
00634 void Quon::handleJoin(GameAPIPositionMessage* gameMsg)
00635 {
00636     TransportAddress joinNode = bootstrapList->getBootstrapNode();
00637     thisSite->position = gameMsg->getPosition();
00638     // check if this is the only node in the overlay
00639     if(joinNode.isUnspecified()) {
00640         changeState(QREADY);
00641     }
00642     else {
00643         QuonMessage* quonMsg = new QuonMessage("JOIN_REQUEST");
00644         quonMsg->setCommand(JOIN_REQUEST);
00645         quonMsg->setSender(thisSite->address);
00646         quonMsg->setPosition(thisSite->position);
00647         quonMsg->setAOIsize(AOIWidth);
00648         quonMsg->setBitLength(QUON_L(quonMsg));
00649         sendMessage(quonMsg, joinNode);
00650     }
00651 }
00652 
00653 void Quon::handleMove(GameAPIPositionMessage* gameMsg)
00654 {
00655     Vector2D position = gameMsg->getPosition();
00656     // send position update to neighbors
00657     QuonMoveMessage* quonMoveMsg = new QuonMoveMessage("NODE_MOVE");
00658     quonMoveMsg->setCommand(NODE_MOVE);
00659     quonMoveMsg->setSender(thisSite->address);
00660     quonMoveMsg->setPosition(thisSite->position);
00661     quonMoveMsg->setAOIsize(AOIWidth);
00662     quonMoveMsg->setNewPosition(position);
00663     quonMoveMsg->setBitLength(QUONMOVE_L(quonMoveMsg));
00664 
00665     QuonMoveMessage* quonMoveBindingMsg = new QuonMoveMessage(*quonMoveMsg);
00666     quonMoveBindingMsg->setNeighborHandleArraySize(Sites.size());
00667     quonMoveBindingMsg->setNeighborPositionArraySize(Sites.size());
00668     int i = 0;
00669     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00670       if(itSites->second->type == QBINDING || itSites->second->softNeighbor ) {
00671         quonMoveBindingMsg->setNeighborHandle(i, itSites->second->address);
00672         quonMoveBindingMsg->setNeighborPosition(i, itSites->second->position);
00673         ++i;
00674       }
00675     }
00676     quonMoveBindingMsg->setNeighborHandleArraySize(i);
00677     quonMoveBindingMsg->setNeighborPositionArraySize(i);
00678     if(i > 0) {
00679       // speedhack:
00680       // instead of building individual MoveMessages for every binding and softstate neighbor,
00681       // we just send all binding/soft to every other binding/soft neighbor and pretend we did not send  neighbors their own neighborslistentry
00682       quonMoveBindingMsg->setBitLength(QUONMOVE_L(quonMoveBindingMsg) - QUONENTRY_L);
00683     }
00684 
00685     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00686       QuonMoveMessage* copyMsg;
00687       if(itSites->second->type == QBINDING || itSites->second->softNeighbor ) {
00688         copyMsg = new QuonMoveMessage(*quonMoveBindingMsg);
00689         if(itSites->second->type == QBINDING) {
00690           copyMsg->setIsBinding(true);
00691         }
00692         else {
00693           ++softConnections;
00694         }
00695       }
00696       else {
00697         copyMsg = new QuonMoveMessage(*quonMoveMsg);
00698       }
00699       sendMessage(copyMsg, itSites->second->address);
00700     }
00701     delete quonMoveMsg;
00702     delete quonMoveBindingMsg;
00703 
00704     // update position
00705     updateThisSite(position);
00706     classifySites();
00707     // update simple client
00708     synchronizeAppNeighbors(QPURGESOFT);
00709     purgeSites(QPURGESOFT);
00710 }
00711 
00712 void Quon::handleEvent(GameAPIMessage* msg)
00713 {
00714     // send event to neighbors
00715     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00716         QuonEventMessage *quonMsg = new QuonEventMessage("EVENT");
00717         quonMsg->setCommand(QUON_EVENT);
00718         quonMsg->encapsulate((cPacket*)msg->dup());
00719         // FIXME: Message length!
00720         sendMessage(quonMsg, itSites->second->address);
00721     }
00722 }
00723 
00724 void Quon::handleJoinRequest(QuonMessage* quonMsg)
00725 {
00726     Vector2D joinPosition = quonMsg->getPosition();
00727     // start with this node
00728     double min_dist = thisSite->position.distanceSqr(joinPosition);
00729     QuonSite* forwardSite = thisSite;
00730     // iterate through all neighbors
00731     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00732         if(itSites->second->position.distanceSqr(joinPosition) < min_dist) { //FIXME: use xy metric if desired?
00733             min_dist = itSites->second->position.distanceSqr(joinPosition);
00734             forwardSite = itSites->second;
00735         }
00736     }
00737 
00738     // do nothing and let node retry with new position if current position is illegal
00739     if(min_dist == 0.0) {
00740         delete quonMsg;
00741     }
00742     else if(forwardSite->type == QTHIS) {
00743         QuonListMessage* quonListMsg = new QuonListMessage("JOIN_ACKNOWLEDGE");
00744         quonListMsg->setCommand(JOIN_ACKNOWLEDGE);
00745         quonListMsg->setSender(thisSite->address);
00746         quonListMsg->setPosition(thisSite->position);
00747         quonListMsg->setAOIsize(AOIWidth);
00748         // fill neighbors list
00749         quonListMsg->setNeighborHandleArraySize(Sites.size());
00750         quonListMsg->setNeighborPositionArraySize(Sites.size());
00751         int i = 0;
00752         for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00753             quonListMsg->setNeighborHandle(i, itSites->second->address);
00754             quonListMsg->setNeighborPosition(i, itSites->second->position);
00755             ++i;
00756         }
00757         quonListMsg->setNeighborHandleArraySize(i);
00758         quonListMsg->setNeighborPositionArraySize(i);
00759 
00760         quonListMsg->setBitLength(QUONLIST_L(quonListMsg));
00761         sendMessage(quonListMsg, quonMsg->getSender());
00762         delete quonMsg;
00763     }
00764     else {
00765         sendMessage(quonMsg, forwardSite->address);
00766     }
00767 }
00768 
00769 void Quon::handleJoinAcknowledge(QuonListMessage* quonListMsg)
00770 {
00771     // add acceptor node
00772     changeState(QREADY);
00773     addSite(quonListMsg->getPosition(), quonListMsg->getSender(), quonListMsg->getAOIsize(), false, QDIRECT);
00774     // add new neighbors
00775     for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
00776         addSite(quonListMsg->getNeighborPosition(i), quonListMsg->getNeighborHandle(i), quonListMsg->getAOIsize());
00777     }
00778     classifySites();
00779     // update simple client
00780     synchronizeAppNeighbors();
00781     purgeSites();
00782     // contact new neighbors
00783     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00784         QuonMoveMessage* quonMoveMsg = new QuonMoveMessage("NODE_MOVE");
00785         quonMoveMsg->setCommand(NODE_MOVE);
00786         quonMoveMsg->setSender(thisSite->address);
00787         quonMoveMsg->setPosition(quonListMsg->getPosition());
00788         quonMoveMsg->setAOIsize(AOIWidth);
00789         quonMoveMsg->setNewPosition(thisSite->position);
00790         if(itSites->second->type == QBINDING) {
00791             quonMoveMsg->setIsBinding(true);
00792         }
00793         quonMoveMsg->setBitLength(QUONMOVE_L(quonMoveMsg));
00794         sendMessage(quonMoveMsg, itSites->second->address);
00795     }
00796     bytesPerSecond = 0.0;
00797 }
00798 
00799 void Quon::handleNodeMove(QuonMoveMessage* quonMoveMsg)
00800 {
00801     RECORD_STATS(
00802             globalStatistics->addStdDev(
00803                 "QuON: MoveDelay",
00804                 SIMTIME_DBL(simTime()) - SIMTIME_DBL(quonMoveMsg->getCreationTime())
00805                 );
00806             );
00807 
00808     QuonAOI oldAOI(quonMoveMsg->getPosition(), quonMoveMsg->getAOIsize(), useSquareMetric);
00809     QuonAOI newAOI(quonMoveMsg->getNewPosition(), quonMoveMsg->getAOIsize(), useSquareMetric);
00810     if(useDynamicAOI) {
00811         QuonSiteMap::iterator itSites = Sites.find(quonMoveMsg->getSender().getKey());
00812         if(itSites != Sites.end() && itSites->second->AOIwidth < quonMoveMsg->getAOIsize()) {
00813             oldAOI.resize(itSites->second->AOIwidth);
00814         }
00815     }
00816 
00817     addSite(quonMoveMsg->getNewPosition(), quonMoveMsg->getSender(), quonMoveMsg->getAOIsize(), quonMoveMsg->getIsBinding(), QDIRECT);
00818     // add new neighbors
00819     handleInvalidNode(quonMoveMsg);
00820     for(unsigned int i=0; i<quonMoveMsg->getNeighborHandleArraySize(); i++) {
00821         addSite(quonMoveMsg->getNeighborPosition(i), quonMoveMsg->getNeighborHandle(i), quonMoveMsg->getAOIsize());
00822     }
00823     classifySites();
00824     // update simple client
00825     synchronizeAppNeighbors();
00826     purgeSites();
00827 
00828     // send new neighbors
00829     QuonListMessage* quonListMsg = new QuonListMessage("NEW_NEIGHBORS");
00830     quonListMsg->setCommand(NEW_NEIGHBORS);
00831     quonListMsg->setSender(thisSite->address);
00832     quonListMsg->setPosition(thisSite->position);
00833     quonListMsg->setAOIsize(AOIWidth);
00834 
00835     quonListMsg->setNeighborHandleArraySize(Sites.size());
00836     quonListMsg->setNeighborPositionArraySize(Sites.size());
00837 
00838     int i = 0;
00839     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00840         if(quonMoveMsg->getSender() != itSites->second->address &&
00841            !oldAOI.collide(itSites->second->position) &&
00842            newAOI.collide(itSites->second->position)) {
00843             quonListMsg->setNeighborHandle(i, itSites->second->address);
00844             quonListMsg->setNeighborPosition(i, itSites->second->position);
00845             ++i;
00846         }
00847     }
00848 
00849     if(i > 0) {
00850         quonListMsg->setNeighborHandleArraySize(i);
00851         quonListMsg->setNeighborPositionArraySize(i);
00852         quonListMsg->setBitLength(QUONLIST_L(quonListMsg));
00853         sendMessage(quonListMsg, quonMoveMsg->getSender());
00854     }
00855     else {
00856         delete quonListMsg;
00857     }
00858 }
00859 
00860 void Quon::handleNewNeighbors(QuonListMessage* quonListMsg)
00861 {
00862     addSite(quonListMsg->getPosition(), quonListMsg->getSender(), quonListMsg->getAOIsize(), false, QDIRECT);
00863 
00864     // add new neighbors
00865     handleInvalidNode(quonListMsg);
00866     for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
00867         addSite(quonListMsg->getNeighborPosition(i), quonListMsg->getNeighborHandle(i), quonListMsg->getAOIsize());
00868     }
00869     classifySites();
00870     // update simple client
00871     synchronizeAppNeighbors();
00872     purgeSites();
00873 }
00874 
00875 void Quon::handleNodeLeave(QuonListMessage* quonListMsg)
00876 {
00877     deleteSite(quonListMsg->getSender());
00878     // update simple client
00879     deleteAppNeighbor(quonListMsg->getSender());
00880 
00881     // insert into delete list
00882     QuonSelfMessage* msg = new QuonSelfMessage("delete_timer");
00883     msg->setKey(quonListMsg->getSender().getKey());
00884     scheduleAt(simTime() + deleteTimeout, msg);
00885     deletedSites.insert(std::make_pair(quonListMsg->getSender().getKey(), msg));
00886 
00887     // add possible new neighbors
00888     handleInvalidNode(quonListMsg);
00889     for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
00890         addSite(quonListMsg->getNeighborPosition(i), quonListMsg->getNeighborHandle(i), quonListMsg->getAOIsize(), true);
00891     }
00892     classifySites();
00893     // update simple client
00894     synchronizeAppNeighbors();
00895     purgeSites();
00896 }
00897 
00898 void Quon::handleInvalidNode(QuonListMessage* quonListMsg)
00899 {
00900     for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
00901         if(deletedSites.find(quonListMsg->getNeighborHandle(i).getKey()) != deletedSites.end()) {
00902             QuonListMessage* quonLeaveMsg = new QuonListMessage("NODE_LEAVE");
00903             quonLeaveMsg->setCommand(NODE_LEAVE);
00904             quonLeaveMsg->setSender(quonListMsg->getNeighborHandle(i));
00905             quonLeaveMsg->setPosition(quonListMsg->getNeighborPosition(i));
00906             quonLeaveMsg->setAOIsize(AOIWidth);
00907             quonLeaveMsg->setNeighborHandleArraySize(Sites.size());
00908             quonLeaveMsg->setNeighborPositionArraySize(Sites.size());
00909             int i = 0;
00910             for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00911                 if(itSites->second->type == QBINDING) {
00912                     quonLeaveMsg->setNeighborHandle(i, itSites->second->address);
00913                     quonLeaveMsg->setNeighborPosition(i, itSites->second->position);
00914                     ++i;
00915                 }
00916             }
00917             quonLeaveMsg->setNeighborHandleArraySize(i);
00918             quonLeaveMsg->setNeighborPositionArraySize(i);
00919             quonLeaveMsg->setBitLength(QUONLIST_L(quonLeaveMsg));
00920             sendMessage(quonLeaveMsg, quonListMsg->getSender());
00921         }
00922     }
00923 }
00924 
00925 void Quon::synchronizeAppNeighbors(QPurgeType purgeSoftSites)
00926 {
00927     GameAPIListMessage* gameMsg = new GameAPIListMessage("NEIGHBOR_UPDATE");
00928     gameMsg->setCommand(NEIGHBOR_UPDATE);
00929 
00930     gameMsg->setRemoveNeighborArraySize(Sites.size());
00931     gameMsg->setAddNeighborArraySize(Sites.size());
00932     gameMsg->setNeighborPositionArraySize(Sites.size());
00933 
00934     int remSize, addSize;
00935     remSize = addSize = 0;
00936     for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
00937         if(itSites->second->type == QUNDEFINED && (purgeSoftSites == QPURGESOFT || !itSites->second->softNeighbor) && itSites->second->dirty) {
00938             gameMsg->setRemoveNeighbor(remSize, itSites->second->address);
00939             ++remSize;
00940         }
00941         else if(itSites->second->dirty) {
00942             gameMsg->setAddNeighbor(addSize, itSites->second->address);
00943             gameMsg->setNeighborPosition(addSize, itSites->second->position);
00944             itSites->second->dirty = false;
00945             ++addSize;
00946         }
00947     }
00948 
00949     if(remSize > 0 || addSize > 0) {
00950         gameMsg->setRemoveNeighborArraySize(remSize);
00951         gameMsg->setAddNeighborArraySize(addSize);
00952         gameMsg->setNeighborPositionArraySize(addSize);
00953         sendToApp(gameMsg);
00954     }
00955     else {
00956         delete gameMsg;
00957     }
00958 }
00959 
00960 void Quon::deleteAppNeighbor(NodeHandle node)
00961 {
00962     GameAPIListMessage* gameMsg = new GameAPIListMessage("NEIGHBOR_UPDATE");
00963     gameMsg->setCommand(NEIGHBOR_UPDATE);
00964     gameMsg->setRemoveNeighborArraySize(1);
00965     gameMsg->setAddNeighborArraySize(0);
00966     gameMsg->setNeighborPositionArraySize(0);
00967     gameMsg->setRemoveNeighbor(0, node);
00968     sendToApp(gameMsg);
00969 }
00970 
00971 void Quon::sendToApp(cMessage* msg)
00972 {
00973     // debug output
00974     if(debugOutput) {
00975         EV << "[Quon::sendToApp() @ " << thisNode.getAddress()
00976            << " (" << thisNode.getKey().toString(16) << ")]\n"
00977            << "    Node " << thisSite->address.getAddress() << " sending " << msg->getName() << " to application."
00978            << endl;
00979     }
00980     send(msg, "appOut");
00981 }
00982 
00983 void Quon::sendMessage(QuonMessage* quonMsg, NodeHandle destination)
00984 {
00985     // collect statistics
00986     RECORD_STATS(
00987         switch(quonMsg->getCommand()) {
00988             case JOIN_REQUEST:
00989                 joinRequestBytesSend += quonMsg->getByteLength();
00990             break;
00991             case JOIN_ACKNOWLEDGE:
00992                 joinAcknowledgeBytesSend += quonMsg->getByteLength();
00993             break;
00994             case NODE_MOVE:
00995                 nodeMoveBytesSend += quonMsg->getByteLength();
00996             break;
00997             case NEW_NEIGHBORS:
00998                 newNeighborsBytesSend += quonMsg->getByteLength();
00999             break;
01000             case NODE_LEAVE:
01001                 nodeLeaveBytesSend += quonMsg->getByteLength();
01002             break;
01003         }
01004         if(qstate == QREADY) {
01005             bytesPerSecond += quonMsg->getByteLength();
01006         }
01007     );
01008 
01009     // debug output
01010     if(debugOutput) {
01011         EV << "[Quon::sendMessage() @ " << thisNode.getAddress()
01012            << " (" << thisNode.getKey().toString(16) << ")]\n"
01013            << "    Node " << thisSite->address.getAddress() << " sending " << quonMsg->getName() << " to " << destination.getAddress() << "."
01014            << endl;
01015     }
01016     sendMessageToUDP(destination, quonMsg);
01017 }
01018 
01019 void Quon::setBootstrapedIcon()
01020 {
01021     if(ev.isGUI()) {
01022         switch(qstate) {
01023             case QUNINITIALIZED:
01024                 getParentModule()->getParentModule()->getDisplayString().setTagArg("i2", 1, "red");
01025                 getDisplayString().setTagArg("i", 1, "red");
01026                 break;
01027             case QJOINING:
01028                 getParentModule()->getParentModule()->getDisplayString().setTagArg("i2", 1, "yellow");
01029                 getDisplayString().setTagArg("i", 1, "yellow");
01030                 break;
01031             case QREADY:
01032                 getParentModule()->getParentModule()->getDisplayString().setTagArg("i2", 1, "green");
01033                 getDisplayString().setTagArg("i", 1, "green");
01034                 break;
01035         }
01036     }
01037 }
01038 
01039 void Quon::finishOverlay()
01040 {
01041     double overallBytesSend = joinRequestBytesSend
01042                             + joinAcknowledgeBytesSend
01043                             + nodeMoveBytesSend
01044                             + newNeighborsBytesSend
01045                             + nodeLeaveBytesSend;
01046     if(overallBytesSend != 0.0) {
01047         // collect statistics in percent
01048         globalStatistics->addStdDev("Quon: fraction of JOIN_REQUEST bytes sent ", joinRequestBytesSend / overallBytesSend);
01049         globalStatistics->addStdDev("Quon: fraction of JOIN_ACKNOWLEDGE bytes sent", joinAcknowledgeBytesSend / overallBytesSend);
01050         globalStatistics->addStdDev("Quon: fraction of NODE_MOVE bytes sent", nodeMoveBytesSend / overallBytesSend);
01051         globalStatistics->addStdDev("Quon: fraction of NEW_NEIGHBORS bytes sent", newNeighborsBytesSend / overallBytesSend);
01052         globalStatistics->addStdDev("Quon: fraction of NODE_LEAVE bytes sent", nodeLeaveBytesSend / overallBytesSend);
01053     }
01054     globalStatistics->addStdDev("Quon: max bytes/second sent", maxBytesPerSecondSend);
01055 
01056 //    We use our own time count to avoid rounding errors
01057 //    simtime_t time = globalStatistics->calcMeasuredLifetime(creationTime);
01058 //    if(time != 0.0) {
01059     if(secTimerCount != 0) {
01060         globalStatistics->addStdDev("Quon: average bytes/second sent", averageBytesPerSecondSend / (double) secTimerCount);
01061         globalStatistics->addStdDev("Quon: average direct-neighbor count", directNeighborCount / (double) secTimerCount);
01062         globalStatistics->addStdDev("Quon: average binding-neighbor count", bindingNeighborCount / (double) secTimerCount);
01063         globalStatistics->addStdDev("Quon: average soft-neighbor count", softNeighborCount / (double) secTimerCount);
01064         //globalStatistics->addStdDev("Quon: average rejoin count", rejoinCount);
01065         globalStatistics->addStdDev("Quon: average AOI width", avgAOI / (double) secTimerCount);
01066     }
01067 
01068     changeState(QUNINITIALIZED);
01069 }
01070 
01071 QState Quon::getState()
01072 {
01073     Enter_Method_Silent();
01074     return qstate;
01075 }
01076 
01077 double Quon::getAOI()
01078 {
01079     Enter_Method_Silent();
01080     return AOIWidth / (double)par("AOIBufferFactor");
01081 }
01082 
01083 Vector2D Quon::getPosition()
01084 {
01085     Enter_Method_Silent();
01086     return thisSite->position;
01087 }
01088 
01089 double Quon::getAreaDimension()
01090 {
01091     Enter_Method_Silent();
01092     return areaDimension;
01093 }
01094 
01095 OverlayKey Quon::getKey()
01096 {
01097     Enter_Method_Silent();
01098     return thisSite->address.getKey();
01099 }
01100 
01101 long Quon::getSoftNeighborCount()
01102 {
01103     Enter_Method_Silent();
01104     long temp = softConnections;
01105     softConnections = 0;
01106     return temp;
01107 }
01108 
01109 Quon::~Quon()
01110 {
01111     // destroy self timer messages
01112     cancelAndDelete(join_timer);
01113     cancelAndDelete(sec_timer);
01114     cancelAndDelete(alive_timer);
01115     cancelAndDelete(backup_timer);
01116     cancelAndDelete(adaption_timer);
01117     delete thisSite;
01118     QuonSiteMap::iterator itSites = Sites.begin();
01119     while(itSites != Sites.end()) {
01120         delete itSites->second;
01121         //Sites.erase(itSites++);
01122         ++itSites++;
01123     }
01124 }

Generated on Tue Sep 8 17:26:54 2009 for OverSim by  doxygen 1.5.8