INET Framework for OMNeT++/OMNEST
AnsaEtherMAC.cc
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2003 CTIE, Monash University
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 
00019 #include <stdio.h>
00020 #include <string.h>
00021 #include <omnetpp.h>
00022 #include "AnsaEtherMAC.h"
00023 #include "Ieee802Ctrl_m.h"
00024 #include "IPassiveQueue.h"
00025 
00026 
00027 
00028 static std::ostream& operator<< (std::ostream& out, cMessage *msg)
00029 {
00030     out << "(" << msg->getClassName() << ")" << msg->getFullName();
00031     return out;
00032 }
00033 
00034 
00035 Define_Module( AnsaEtherMAC );
00036 
00037 AnsaEtherMAC::AnsaEtherMAC()
00038 {
00039     frameBeingReceived = NULL;
00040     endJammingMsg = endRxMsg = endBackoffMsg = NULL;
00041 }
00042 
00043 AnsaEtherMAC::~AnsaEtherMAC()
00044 {
00045     delete frameBeingReceived;
00046     cancelAndDelete(endRxMsg);
00047     cancelAndDelete(endBackoffMsg);
00048     cancelAndDelete(endJammingMsg);
00049 }
00050 
00051 void AnsaEtherMAC::initialize()
00052 {
00053     AnsaEtherMACBase::initialize();
00054 
00055     endRxMsg = new cMessage("EndReception", ENDRECEPTION);
00056     endBackoffMsg = new cMessage("EndBackoff", ENDBACKOFF);
00057     endJammingMsg = new cMessage("EndJamming", ENDJAMMING);
00058 
00059     // check: datarate is forbidden with AnsaEtherMAC -- module's txrate must be used
00060     cGate *g = physOutGate;
00061     while (g)
00062     {
00063         cDatarateChannel *chan = dynamic_cast<cDatarateChannel*>(g->getChannel());
00064         if (chan && chan->par("datarate").doubleValue()>0)
00065             error("connection on gate %s has data rate set: using data rate with AnsaEtherMAC "
00066                   "is forbidden, module's txrate parameter must be used instead",
00067                   g->getFullPath().c_str());
00068         g = g->getNextGate();
00069     }
00070 
00071     // launch autoconfig process
00072     bool performAutoconfig = true;
00073     if (!disabled && connected && performAutoconfig)
00074     {
00075         startAutoconfig();
00076     }
00077     else
00078     {
00079         autoconfigInProgress = false;
00080         duplexMode = par("duplexEnabled");
00081         calculateParameters();
00082     }
00083     WATCH(autoconfigInProgress);
00084 
00085     // initialize state info
00086     backoffs = 0;
00087     numConcurrentTransmissions = 0;
00088 
00089     WATCH(backoffs);
00090     WATCH(numConcurrentTransmissions);
00091 
00092     // initialize statistics
00093     totalCollisionTime = 0.0;
00094     totalSuccessfulRxTxTime = 0.0;
00095     numCollisions = numBackoffs = 0;
00096 
00097     WATCH(numCollisions);
00098     WATCH(numBackoffs);
00099 
00100     numCollisionsVector.setName("collisions");
00101     numBackoffsVector.setName("backoffs");
00102 }
00103 
00104 void AnsaEtherMAC::initializeTxrate()
00105 {
00106     txrate = par("txrate");
00107 }
00108 
00109 void AnsaEtherMAC::startAutoconfig()
00110 {
00111     autoconfigInProgress = true;
00112     lowestTxrateSuggested = 0;  // none suggested
00113     duplexVetoed = false;
00114 
00115     double initialTxrate = par("txrate");
00116     bool duplexEnabled = par("duplexEnabled");
00117     txrate = 0;
00118     duplexMode = duplexEnabled;
00119     if (!duplexEnabled || initialTxrate>0)
00120     {
00121         EV << "Autoconfig: advertising our settings: " << initialTxrate/1000000 << "Mb, "
00122            << (duplexMode ? "duplex" : "half-duplex") << endl;
00123 
00124         EtherAutoconfig *autoconf = new EtherAutoconfig("autoconf");
00125         if (!duplexEnabled)
00126             autoconf->setHalfDuplex(true);
00127         if (initialTxrate>0)
00128             autoconf->setTxrate(initialTxrate);
00129         send(autoconf, physOutGate);
00130     }
00131     scheduleAt(simTime()+AUTOCONFIG_PERIOD, new cMessage("EndAutoconfig",ENDAUTOCONFIG));
00132 }
00133 
00134 void AnsaEtherMAC::handleAutoconfigMessage(cMessage *msg)
00135 {
00136     if (!msg->isSelfMessage())
00137     {
00138         if (msg->getArrivalGate() == gate("upperLayerIn"))
00139         {
00140             // from upper layer
00141             EV << "Received frame from upper layer during autoconfig period: " << msg << endl;
00142             processFrameFromUpperLayer(check_and_cast<EtherFrame *>(msg));
00143         }
00144         else
00145         {
00146             // from network: must be autoconfig message
00147             EV << "Message from network during autoconfig period: " << msg << endl;
00148             EtherAutoconfig *autoconf = check_and_cast<EtherAutoconfig *>(msg);
00149             double acTxrate = autoconf->getTxrate();
00150 
00151             EV << "Autoconfig message: ";
00152             if (acTxrate>0)
00153                 EV << acTxrate/1000000 << "Mb ";
00154             if (autoconf->getHalfDuplex())
00155                 EV << "non-duplex";
00156             EV << "\n";
00157 
00158             if (acTxrate>0 && (acTxrate<lowestTxrateSuggested || lowestTxrateSuggested==0))
00159                 lowestTxrateSuggested = acTxrate;
00160             if (!duplexVetoed && autoconf->getHalfDuplex())
00161                 duplexVetoed = true;
00162             delete msg;
00163         }
00164     }
00165     else
00166     {
00167         // self-message signals end of autoconfig period
00168         EV << "Self-message during autoconfig period: " << msg << endl;
00169 
00170         delete msg;
00171         autoconfigInProgress = false;
00172 
00173         double initialTxrate = par("txrate");
00174         bool duplexEnabled = par("duplexEnabled");
00175 
00176         txrate = (initialTxrate==0 && lowestTxrateSuggested==0) ? 100000000 /* 100 Mb */:
00177                  (initialTxrate==0) ? lowestTxrateSuggested :
00178                  (lowestTxrateSuggested==0) ? initialTxrate :
00179                  (lowestTxrateSuggested<initialTxrate) ? lowestTxrateSuggested : initialTxrate;
00180         duplexMode = (duplexEnabled && !duplexVetoed);
00181         calculateParameters();
00182 
00183         EV << "Parameters after autoconfig: txrate=" << txrate/1000000 << "Mb, " << (duplexMode ? "duplex" : "half-duplex") << endl;
00184 
00185         if (ev.isGUI())
00186         {
00187             char modestr[64];
00188             sprintf(modestr, "%dMb\n%s", int(txrate/1000000), (duplexMode ? "full duplex" : "half duplex"));
00189             getDisplayString().setTagArg("t",0,modestr);
00190             //getDisplayString().setTagArg("t",1,"r");
00191             sprintf(modestr, "%s: %dMb %s", getFullName(), int(txrate/1000000), (duplexMode ? "duplex" : "half duplex"));
00192             getParentModule()->bubble(modestr);
00193         }
00194 
00195         if (!txQueue.empty())
00196         {
00197             EV << "Autoconfig period over, starting to send frames\n";
00198             scheduleEndIFGPeriod();
00199         }
00200     }
00201 }
00202 
00203 void AnsaEtherMAC::handleMessage (cMessage *msg)
00204 {
00205     if (disabled)
00206     {
00207         EV << "MAC is disabled -- dropping message " << msg << "\n";
00208         delete msg;
00209         return;
00210     }
00211     if (autoconfigInProgress)
00212     {
00213         handleAutoconfigMessage(msg);
00214         return;
00215     }
00216 
00217     printState();
00218     // some consistency check
00219     if (!duplexMode && transmitState==TRANSMITTING_STATE && receiveState!=RX_IDLE_STATE)
00220         error("Inconsistent state -- transmitting and receiving at the same time");
00221 
00222     if (!msg->isSelfMessage())
00223     {
00224         // either frame from upper layer, or frame/jam signal from the network
00225         if (msg->getArrivalGate() == gate("upperLayerIn"))
00226             processFrameFromUpperLayer(check_and_cast<EtherFrame *>(msg));
00227         else
00228             processMsgFromNetwork(PK(msg));
00229     }
00230     else
00231     {
00232         // Process different self-messages (timer signals)
00233         EV << "Self-message " << msg << " received\n";
00234         switch (msg->getKind())
00235         {
00236             case ENDIFG:
00237                 handleEndIFGPeriod();
00238                 break;
00239 
00240             case ENDTRANSMISSION:
00241                 handleEndTxPeriod();
00242                 break;
00243 
00244             case ENDRECEPTION:
00245                 handleEndRxPeriod();
00246                 break;
00247 
00248             case ENDBACKOFF:
00249                 handleEndBackoffPeriod();
00250                 break;
00251 
00252             case ENDJAMMING:
00253                 handleEndJammingPeriod();
00254                 break;
00255 
00256             case ENDPAUSE:
00257                 handleEndPausePeriod();
00258                 break;
00259 
00260             default:
00261                 error("self-message with unexpected message kind %d", msg->getKind());
00262         }
00263     }
00264     printState();
00265 
00266     if (ev.isGUI())
00267         updateDisplayString();
00268 }
00269 
00270 
00271 void AnsaEtherMAC::processFrameFromUpperLayer(EtherFrame *frame)
00272 {
00273     AnsaEtherMACBase::processFrameFromUpperLayer(frame);
00274 
00275     if (!autoconfigInProgress && (duplexMode || receiveState==RX_IDLE_STATE) && transmitState==TX_IDLE_STATE)
00276     {
00277         EV << "No incoming carrier signals detected, frame clear to send, wait IFG first\n";
00278         scheduleEndIFGPeriod();
00279     }
00280 }
00281 
00282 
00283 void AnsaEtherMAC::processMsgFromNetwork(cPacket *msg)
00284 {
00285     AnsaEtherMACBase::processMsgFromNetwork(msg);
00286 
00287     simtime_t endRxTime = simTime() + msg->getBitLength()*bitTime;
00288 
00289     if (!duplexMode && transmitState==TRANSMITTING_STATE)
00290     {
00291         // since we're halfduplex, receiveState must be RX_IDLE_STATE (asserted at top of handleMessage)
00292         if (dynamic_cast<EtherJam*>(msg) != NULL)
00293             error("Stray jam signal arrived while transmitting (usual cause is cable length exceeding allowed maximum)");
00294 
00295         EV << "Transmission interrupted by incoming frame, handling collision\n";
00296         cancelEvent(endTxMsg);
00297 
00298         EV << "Transmitting jam signal\n";
00299         sendJamSignal(); // backoff will be executed when jamming finished
00300 
00301         // set receive state and schedule end of reception
00302         receiveState = RX_COLLISION_STATE;
00303         numConcurrentTransmissions++;
00304         simtime_t endJamTime = simTime()+jamDuration;
00305         scheduleAt(endRxTime<endJamTime ? endJamTime : endRxTime, endRxMsg);
00306         delete msg;
00307 
00308         numCollisions++;
00309         numCollisionsVector.record(numCollisions);
00310     }
00311     else if (receiveState==RX_IDLE_STATE)
00312     {
00313         if (dynamic_cast<EtherJam*>(msg) != NULL)
00314             error("Stray jam signal arrived (usual cause is cable length exceeding allowed maximum)");
00315 
00316         EV << "Start reception of frame\n";
00317         numConcurrentTransmissions++;
00318         if (frameBeingReceived)
00319             error("frameBeingReceived!=0 in RX_IDLE_STATE");
00320         frameBeingReceived = (EtherFrame *)msg;
00321         scheduleEndRxPeriod(msg);
00322         channelBusySince = simTime();
00323     }
00324     else if (receiveState==RECEIVING_STATE && dynamic_cast<EtherJam*>(msg)==NULL && endRxMsg->getArrivalTime()-simTime()<bitTime)
00325     {
00326         // With the above condition we filter out "false" collisions that may occur with
00327         // back-to-back frames. That is: when "beginning of frame" message (this one) occurs
00328         // BEFORE "end of previous frame" event (endRxMsg) -- same simulation time,
00329         // only wrong order.
00330 
00331         EV << "Back-to-back frames: completing reception of current frame, starting reception of next one\n";
00332 
00333         // complete reception of previous frame
00334         cancelEvent(endRxMsg);
00335         EtherFrame *frame = frameBeingReceived;
00336         frameBeingReceived = NULL;
00337         frameReceptionComplete(frame);
00338 
00339         // start receiving next frame
00340         frameBeingReceived = (EtherFrame *)msg;
00341         scheduleEndRxPeriod(msg);
00342     }
00343     else // (receiveState==RECEIVING_STATE || receiveState==RX_COLLISION_STATE)
00344     {
00345         // handle overlapping receptions
00346         if (dynamic_cast<EtherJam*>(msg) != NULL)
00347         {
00348             if (numConcurrentTransmissions<=0)
00349                 error("numConcurrentTransmissions=%d on jam arrival (stray jam?)",numConcurrentTransmissions);
00350 
00351             numConcurrentTransmissions--;
00352             EV << "Jam signal received, this marks end of one transmission\n";
00353 
00354             // by the time jamming ends, all transmissions will have been aborted
00355             if (numConcurrentTransmissions==0)
00356             {
00357                 EV << "Last jam signal received, collision will ends when jam ends\n";
00358                 cancelEvent(endRxMsg);
00359                 scheduleAt(endRxTime, endRxMsg);
00360             }
00361         }
00362         else // EtherFrame or EtherPauseFrame
00363         {
00364             numConcurrentTransmissions++;
00365             if (endRxMsg->getArrivalTime() < endRxTime)
00366             {
00367                 // otherwise just wait until the end of the longest transmission
00368                 EV << "Overlapping receptions -- setting collision state and extending collision period\n";
00369                 cancelEvent(endRxMsg);
00370                 scheduleAt(endRxTime, endRxMsg);
00371             }
00372             else
00373             {
00374                 EV << "Overlapping receptions -- setting collision state\n";
00375             }
00376         }
00377 
00378         // delete collided frames: arrived frame as well as the one we're currently receiving
00379         delete msg;
00380         if (receiveState==RECEIVING_STATE)
00381         {
00382             delete frameBeingReceived;
00383             frameBeingReceived = NULL;
00384 
00385             numCollisions++;
00386             numCollisionsVector.record(numCollisions);
00387         }
00388 
00389         // go to collision state
00390         receiveState = RX_COLLISION_STATE;
00391     }
00392 }
00393 
00394 
00395 void AnsaEtherMAC::handleEndIFGPeriod()
00396 {
00397     AnsaEtherMACBase::handleEndIFGPeriod();
00398 
00399     // End of IFG period, okay to transmit, if Rx idle OR duplexMode
00400     cPacket *frame = (cPacket *)txQueue.front();
00401 
00402     // Perform carrier extension if in Gigabit Ethernet
00403     if (carrierExtension && frame->getByteLength() < GIGABIT_MIN_FRAME_WITH_EXT)
00404     {
00405         EV << "Performing carrier extension of small frame\n";
00406         frame->setByteLength(GIGABIT_MIN_FRAME_WITH_EXT);
00407     }
00408 
00409     // send frame to network
00410     startFrameTransmission();
00411 }
00412 
00413 void AnsaEtherMAC::startFrameTransmission()
00414 {
00415     cPacket *origFrame = (cPacket *)txQueue.front();
00416     EV << "Transmitting a copy of frame " << origFrame << endl;
00417     cPacket *frame = origFrame->dup();
00418 
00419     // add preamble and SFD (Starting Frame Delimiter), then send out
00420     frame->addByteLength(PREAMBLE_BYTES+SFD_BYTES);
00421     if (ev.isGUI())  updateConnectionColor(TRANSMITTING_STATE);
00422     send(frame, physOutGate);
00423 
00424     // update burst variables
00425     if (frameBursting)
00426     {
00427         bytesSentInBurst = frame->getByteLength();
00428         framesSentInBurst++;
00429     }
00430 
00431     // check for collisions (there might be an ongoing reception which we don't know about, see below)
00432     if (!duplexMode && receiveState!=RX_IDLE_STATE)
00433     {
00434         // During the IFG period the hardware cannot listen to the channel,
00435         // so it might happen that receptions have begun during the IFG,
00436         // and even collisions might be in progress.
00437         //
00438         // But we don't know of any ongoing transmission so we blindly
00439         // start transmitting, immediately collide and send a jam signal.
00440         //
00441         sendJamSignal();
00442         // numConcurrentTransmissions stays the same: +1 transmission, -1 jam
00443 
00444         if (receiveState==RECEIVING_STATE)
00445         {
00446             delete frameBeingReceived;
00447             frameBeingReceived = NULL;
00448 
00449             numCollisions++;
00450             numCollisionsVector.record(numCollisions);
00451         }
00452         // go to collision state
00453         receiveState = RX_COLLISION_STATE;
00454     }
00455     else
00456     {
00457         // no collision
00458         scheduleEndTxPeriod(frame);
00459 
00460         // only count transmissions in totalSuccessfulRxTxTime if channel is half-duplex
00461         if (!duplexMode)
00462             channelBusySince = simTime();
00463     }
00464 }
00465 
00466 void AnsaEtherMAC::handleEndTxPeriod()
00467 {
00468     AnsaEtherMACBase::handleEndTxPeriod();
00469 
00470     // only count transmissions in totalSuccessfulRxTxTime if channel is half-duplex
00471     if (!duplexMode)
00472     {
00473         simtime_t dt = simTime()-channelBusySince;
00474         totalSuccessfulRxTxTime += dt;
00475     }
00476 
00477     backoffs = 0;
00478 
00479     // check for and obey received PAUSE frames after each transmission
00480     if (checkAndScheduleEndPausePeriod())
00481         return;
00482 
00483     // Gigabit Ethernet: now decide if we transmit next frame right away (burst) or wait IFG
00484     // FIXME! this is not entirely correct, there must be IFG between burst frames too
00485     bool burstFrame=false;
00486     if (frameBursting && !txQueue.empty())
00487     {
00488         // check if max bytes for burst not exceeded
00489         if (bytesSentInBurst<GIGABIT_MAX_BURST_BYTES)
00490         {
00491              burstFrame=true;
00492              EV << "Transmitting next frame in current burst\n";
00493         }
00494         else
00495         {
00496              EV << "Next frame does not fit in current burst\n";
00497         }
00498     }
00499 
00500     if (burstFrame)
00501         startFrameTransmission();
00502     else
00503         beginSendFrames();
00504 }
00505 
00506 void AnsaEtherMAC::handleEndRxPeriod()
00507 {
00508     EV << "Frame reception complete\n";
00509     simtime_t dt = simTime()-channelBusySince;
00510     if (receiveState==RECEIVING_STATE) // i.e. not RX_COLLISION_STATE
00511     {
00512         EtherFrame *frame = frameBeingReceived;
00513         frameBeingReceived = NULL;
00514         frameReceptionComplete(frame);
00515         totalSuccessfulRxTxTime += dt;
00516     }
00517     else
00518     {
00519         totalCollisionTime += dt;
00520     }
00521 
00522     receiveState = RX_IDLE_STATE;
00523     numConcurrentTransmissions = 0;
00524 
00525     if (transmitState==TX_IDLE_STATE && !txQueue.empty())
00526     {
00527         EV << "Receiver now idle, can transmit frames in output buffer after IFG period\n";
00528         scheduleEndIFGPeriod();
00529     }
00530 }
00531 
00532 void AnsaEtherMAC::handleEndBackoffPeriod()
00533 {
00534     if (transmitState != BACKOFF_STATE)
00535         error("At end of BACKOFF not in BACKOFF_STATE!");
00536     if (txQueue.empty())
00537         error("At end of BACKOFF and buffer empty!");
00538 
00539     if (receiveState==RX_IDLE_STATE)
00540     {
00541         EV << "Backoff period ended, wait IFG\n";
00542         scheduleEndIFGPeriod();
00543     }
00544     else
00545     {
00546         EV << "Backoff period ended but channel not free, idling\n";
00547         transmitState = TX_IDLE_STATE;
00548     }
00549 }
00550 
00551 void AnsaEtherMAC::handleEndJammingPeriod()
00552 {
00553     if (transmitState != JAMMING_STATE)
00554         error("At end of JAMMING not in JAMMING_STATE!");
00555     EV << "Jamming finished, executing backoff\n";
00556     handleRetransmission();
00557 }
00558 
00559 void AnsaEtherMAC::sendJamSignal()
00560 {
00561     cPacket *jam = new EtherJam("JAM_SIGNAL");
00562     jam->setByteLength(JAM_SIGNAL_BYTES);
00563     if (ev.isGUI())  updateConnectionColor(JAMMING_STATE);
00564     send(jam, physOutGate);
00565 
00566     scheduleAt(simTime()+jamDuration, endJammingMsg);
00567     transmitState = JAMMING_STATE;
00568 }
00569 
00570 void AnsaEtherMAC::scheduleEndRxPeriod(cPacket *frame)
00571 {
00572     scheduleAt(simTime()+frame->getBitLength()*bitTime, endRxMsg);
00573     receiveState = RECEIVING_STATE;
00574 }
00575 
00576 void AnsaEtherMAC::handleRetransmission()
00577 {
00578     if (++backoffs > MAX_ATTEMPTS)
00579     {
00580         EV << "Number of retransmit attempts of frame exceeds maximum, cancelling transmission of frame\n";
00581         delete txQueue.pop();
00582 
00583         transmitState = TX_IDLE_STATE;
00584         backoffs = 0;
00585         // no beginSendFrames(), because end of jam signal sending will trigger it automatically
00586         return;
00587     }
00588 
00589     EV << "Executing backoff procedure\n";
00590     int backoffrange = (backoffs>=BACKOFF_RANGE_LIMIT) ? 1024 : (1 << backoffs);
00591     int slotNumber = intuniform(0,backoffrange-1);
00592     simtime_t backofftime = slotNumber*slotTime;
00593 
00594     scheduleAt(simTime()+backofftime, endBackoffMsg);
00595     transmitState = BACKOFF_STATE;
00596 
00597     numBackoffs++;
00598     numBackoffsVector.record(numBackoffs);
00599 }
00600 
00601 void AnsaEtherMAC::printState()
00602 {
00603 #define CASE(x) case x: EV << #x; break
00604     EV << "transmitState: ";
00605     switch (transmitState) {
00606         CASE(TX_IDLE_STATE);
00607         CASE(WAIT_IFG_STATE);
00608         CASE(TRANSMITTING_STATE);
00609         CASE(JAMMING_STATE);
00610         CASE(BACKOFF_STATE);
00611         CASE(PAUSE_STATE);
00612     }
00613     EV << ",  receiveState: ";
00614     switch (receiveState) {
00615         CASE(RX_IDLE_STATE);
00616         CASE(RECEIVING_STATE);
00617         CASE(RX_COLLISION_STATE);
00618     }
00619     EV << ",  backoffs: " << backoffs;
00620     EV << ",  numConcurrentTransmissions: " << numConcurrentTransmissions;
00621     EV << ",  queueLength: " << txQueue.length() << endl;
00622 #undef CASE
00623 }
00624 
00625 void AnsaEtherMAC::finish()
00626 {
00627     AnsaEtherMACBase::finish();
00628 
00629     simtime_t t = simTime();
00630     simtime_t totalChannelIdleTime = t - totalSuccessfulRxTxTime - totalCollisionTime;
00631     recordScalar("rx channel idle (%)", 100*(totalChannelIdleTime/t));
00632     recordScalar("rx channel utilization (%)", 100*(totalSuccessfulRxTxTime/t));
00633     recordScalar("rx channel collision (%)", 100*(totalCollisionTime/t));
00634     recordScalar("collisions",     numCollisions);
00635     recordScalar("backoffs",       numBackoffs);
00636 }
00637 
00638 void AnsaEtherMAC::updateHasSubcribers()
00639 {
00640     hasSubscribers = false;  // currently we don't fire any notifications
00641 }
00642