KBRTestApp.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 
00024 #include <IPAddressResolver.h>
00025 #include <CommonMessages_m.h>
00026 #include <GlobalStatistics.h>
00027 #include <UnderlayConfigurator.h>
00028 #include <GlobalNodeList.h>
00029 
00030 #include "KBRTestApp.h"
00031 #include "KBRTestMessage_m.h"
00032 
00033 Define_Module(KBRTestApp);
00034 
00035 KBRTestApp::KBRTestApp()
00036 {
00037     onewayTimer = NULL;
00038 }
00039 
00040 KBRTestApp::~KBRTestApp()
00041 {
00042     cancelAndDelete(onewayTimer);
00043     cancelAndDelete(rpcTimer);
00044     cancelAndDelete(lookupTimer);
00045 }
00046 
00047 void KBRTestApp::initializeApp(int stage)
00048 {
00049     if (stage != MIN_STAGE_APP) {
00050         return;
00051     }
00052 
00053     kbrOneWayTest = par("kbrOneWayTest");
00054     kbrRpcTest = par("kbrRpcTest");
00055     kbrLookupTest = par("kbrLookupTest");
00056 
00057     if (!kbrOneWayTest && !kbrRpcTest && !kbrLookupTest) {
00058         throw cRuntimeError("KBRTestApp::initializeApp(): "
00059                             "no tests are configured!");
00060     }
00061 
00062     failureLatency = par("failureLatency");
00063 
00064     testMsgSize = par("testMsgSize");
00065     lookupNodeIds = par("lookupNodeIds");
00066     mean = par("testMsgInterval");
00067     deviation = mean / 10;
00068     activeNetwInitPhase = par("activeNetwInitPhase");
00069     msgHandleBufSize = par("msgHandleBufSize");
00070     onlyLookupInoffensiveNodes = par("onlyLookupInoffensiveNodes");
00071 
00072     numSent = 0;
00073     bytesSent = 0;
00074     numDelivered = 0;
00075     bytesDelivered = 0;
00076     numDropped = 0;
00077     bytesDropped = 0;
00078     WATCH(numSent);
00079     WATCH(bytesSent);
00080     WATCH(numDelivered);
00081     WATCH(bytesDelivered);
00082     WATCH(numDropped);
00083     WATCH(bytesDropped);
00084 
00085     numRpcSent = 0;
00086     bytesRpcSent = 0;
00087     numRpcDelivered = 0;
00088     bytesRpcDelivered = 0;
00089     numRpcDropped = 0;
00090     bytesRpcDropped = 0;
00091     rpcSuccLatencyCount = 0;
00092     rpcSuccLatencySum = 0;
00093     rpcTotalLatencyCount = 0;
00094     rpcTotalLatencySum = 0;
00095     WATCH(numRpcSent);
00096     WATCH(bytesRpcSent);
00097     WATCH(numRpcDelivered);
00098     WATCH(bytesRpcDelivered);
00099     WATCH(numRpcDropped);
00100     WATCH(bytesRpcDropped);
00101 
00102     numLookupSent = 0;
00103     numLookupSuccess = 0;
00104     numLookupFailed = 0;
00105     WATCH(numLookupSent);
00106     WATCH(numLookupSuccess);
00107     WATCH(numLookupFailed);
00108 
00109     sequenceNumber = 0;
00110 
00111     nodeIsLeavingSoon = false;
00112 
00113     // initialize circular buffer
00114     if (msgHandleBufSize > 0) {
00115         mhBuf.resize(msgHandleBufSize);
00116         mhBufBegin = mhBuf.begin();
00117         mhBufEnd = mhBuf.end();
00118         mhBufNext = mhBufBegin;
00119     }
00120 
00121 #if 0
00122     bindToPort(1025);
00123     thisNode.setPort(1025);
00124 #endif
00125 
00126     // start periodic timer
00127     onewayTimer = new cMessage("onewayTimer");
00128     rpcTimer = new cMessage("rpcTimer");
00129     lookupTimer = new cMessage("lookupTimer");
00130 
00131     if (kbrOneWayTest) {
00132         scheduleAt(simTime() + truncnormal(mean, deviation), onewayTimer);
00133     }
00134     if (kbrRpcTest) {
00135         scheduleAt(simTime() + truncnormal(mean, deviation), rpcTimer);
00136     }
00137     if (kbrLookupTest) {
00138         scheduleAt(simTime() + truncnormal(mean, deviation), lookupTimer);
00139     }
00140 }
00141 
00142 void KBRTestApp::handleTimerEvent(cMessage* msg)
00143 {
00144     // schedule next timer event
00145     scheduleAt(simTime() + truncnormal(mean, deviation), msg);
00146 
00147     // do nothing if the network is still in the initialization phase
00148     if ((!activeNetwInitPhase && underlayConfigurator->isInInitPhase())
00149             || underlayConfigurator->isSimulationEndingSoon()
00150             || nodeIsLeavingSoon) {
00151         return;
00152     }
00153 
00154     std::pair<OverlayKey,TransportAddress> dest = createDestKey();
00155 
00156     if (msg == onewayTimer) {
00157         // TEST 1: route a test message to a key (one-way)
00158         // do nothing if there are currently no nodes in the network
00159         if (!dest.first.isUnspecified()) {
00160             // create a 100 byte test message
00161             KBRTestMessage* testMsg = new KBRTestMessage("KBRTestMessage");
00162             testMsg->setId(getId());
00163             testMsg->setSeqNum(sequenceNumber++);
00164             testMsg->setByteLength(testMsgSize);
00165             testMsg->setMeasurementPhase(globalStatistics->isMeasuring());
00166 
00167             RECORD_STATS(globalStatistics->sentKBRTestAppMessages++;
00168                          numSent++; bytesSent += testMsg->getByteLength());
00169 
00170             callRoute(dest.first, testMsg);
00171         }
00172     } else if (msg == rpcTimer) {
00173         // TEST 2: send a remote procedure call to a specific key and wait for a response
00174         // do nothing if there are currently no nodes in the network
00175         if (!dest.first.isUnspecified()) {
00176             KbrTestCall* call = new KbrTestCall;
00177             call->setByteLength(testMsgSize);
00178             KbrRpcContext* context = new KbrRpcContext;
00179             context->setDestKey(dest.first);
00180             if (lookupNodeIds) {
00181                 context->setDestAddr(dest.second);
00182             }
00183             context->setMeasurementPhase(globalStatistics->isMeasuring());
00184 
00185             RECORD_STATS(numRpcSent++;
00186                          bytesRpcSent += call->getByteLength());
00187 
00188             sendRouteRpcCall(TIER1_COMP, dest.first, call, context);
00189         }
00190     } else /*if (msg == lookupTimer &&)*/ {
00191         // TEST 3: perform a lookup of a specific key
00192         // do nothing if there are currently no nodes in the network
00193         if (!dest.first.isUnspecified()) {
00194             LookupCall* call = new LookupCall();
00195             call->setKey(dest.first);
00196             call->setNumSiblings(overlay->getMaxNumSiblings());
00197             KbrRpcContext* context = new KbrRpcContext;
00198             context->setDestKey(dest.first);
00199             if (lookupNodeIds) {
00200                 context->setDestAddr(dest.second);
00201             }
00202             context->setMeasurementPhase(globalStatistics->isMeasuring());
00203             sendInternalRpcCall(OVERLAY_COMP, call, context);
00204 
00205             RECORD_STATS(numLookupSent++);
00206         }
00207     }
00208 
00209 #if 0
00210     thisNode.setPort(1025);
00211     NodeHandle handle = globalNodeList->getBootstrapNode();
00212     handle.setPort(1025);
00213     pingNode(handle, -1, -1, NULL, "TestPING", NULL, -1, UDP_TRANSPORT);
00214 #endif
00215 
00216 }
00217 void KBRTestApp::pingResponse(PingResponse* response, cPolymorphic* context,
00218                               int rpcId, simtime_t rtt)
00219 {
00220     //std::cout << rtt << std::endl;
00221 }
00222 bool KBRTestApp::handleRpcCall(BaseCallMessage* msg)
00223 {
00224     RPC_SWITCH_START( msg );
00225         RPC_DELEGATE( KbrTest, kbrTestCall );
00226     RPC_SWITCH_END( );
00227 
00228     return RPC_HANDLED;
00229 }
00230 void KBRTestApp::kbrTestCall(KbrTestCall* call)
00231 {
00232     KbrTestResponse* response = new KbrTestResponse;
00233     response->setByteLength(call->getByteLength());
00234     sendRpcResponse(call, response);
00235 }
00236 
00237 void KBRTestApp::handleRpcResponse(BaseResponseMessage* msg,
00238                                    cPolymorphic* context, int rpcId,
00239                                    simtime_t rtt)
00240 {
00241     RPC_SWITCH_START(msg)
00242     RPC_ON_RESPONSE(Lookup) {
00243         EV << "[KBRTestApp::handleRpcResponse() @ " << overlay->getThisNode().getIp()
00244            << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00245            << "    Lookup RPC Response received: id=" << rpcId << "\n"
00246            << "    msg=" << *_LookupResponse << " rtt=" << rtt
00247            << endl;
00248         handleLookupResponse(_LookupResponse, context, rtt);
00249         break;
00250     }
00251     RPC_ON_RESPONSE(KbrTest) {
00252         KbrRpcContext* kbrRpcContext = check_and_cast<KbrRpcContext*>(context);
00253         if (kbrRpcContext->getMeasurementPhase() == true) {
00254             if (!lookupNodeIds ||
00255                 (kbrRpcContext->getDestKey() == msg->getSrcNode().getKey() &&
00256                  kbrRpcContext->getDestAddr() == msg->getSrcNode())) {
00257 
00258                 RECORD_STATS(numRpcDelivered++;
00259                              bytesRpcDelivered += msg->getByteLength());
00260                 RECORD_STATS(globalStatistics->recordOutVector(
00261                         "KBRTestApp: RPC Success Latency", SIMTIME_DBL(rtt)));
00262                 RECORD_STATS(globalStatistics->recordOutVector(
00263                         "KBRTestApp: RPC Total Latency", SIMTIME_DBL(rtt)));
00264                 RECORD_STATS(rpcSuccLatencyCount++;
00265                              rpcSuccLatencySum += SIMTIME_DBL(rtt));
00266                 RECORD_STATS(rpcTotalLatencyCount++;
00267                              rpcTotalLatencySum += SIMTIME_DBL(rtt));
00268                 OverlayCtrlInfo* overlayCtrlInfo =
00269                         dynamic_cast<OverlayCtrlInfo*>(msg->getControlInfo());
00270 
00271                 uint16_t hopSum = msg->getCallHopCount();
00272                 hopSum += (overlayCtrlInfo ? overlayCtrlInfo->getHopCount() : 1);
00273                 RECORD_STATS(globalStatistics->recordOutVector(
00274                         "KBRTestApp: RPC Hop Count", hopSum));
00275 //              RECORD_STATS(globalStatistics->recordHistogram(
00276 //                      "KBRTestApp: RPC Hop Count Histogram", hopSum));
00277             } else {
00278                 RECORD_STATS(numRpcDropped++;
00279                              bytesRpcDropped += msg->getByteLength());
00280                 // for failed RPCs add failureLatency to latency statistics vector
00281                 RECORD_STATS(globalStatistics->recordOutVector(
00282                         "KBRTestApp: RPC Total Latency",
00283                         SIMTIME_DBL(failureLatency)));
00284                 RECORD_STATS(rpcTotalLatencyCount++;
00285                              rpcTotalLatencySum += SIMTIME_DBL(failureLatency));
00286             }
00287         }
00288         delete kbrRpcContext;
00289         break;
00290     }
00291     RPC_SWITCH_END( )
00292 }
00293 
00294 void KBRTestApp::handleRpcTimeout(BaseCallMessage* msg,
00295                                   const TransportAddress& dest,
00296                                   cPolymorphic* context, int rpcId,
00297                                   const OverlayKey& destKey)
00298 {
00299     RPC_SWITCH_START(msg)
00300     RPC_ON_CALL(KbrTest) {
00301         KbrRpcContext* kbrRpcContext = check_and_cast<KbrRpcContext*>(context);
00302         if (kbrRpcContext->getMeasurementPhase() == true) {
00303              RECORD_STATS(numRpcDropped++;
00304                           bytesRpcDropped += msg->getByteLength());
00305              // for failed RPCs add failureLatency to latency statistics vector
00306              RECORD_STATS(globalStatistics->recordOutVector(
00307                          "KBRTestApp: RPC Total Latency",
00308                          SIMTIME_DBL(failureLatency)));
00309              RECORD_STATS(rpcTotalLatencyCount++;
00310                           rpcTotalLatencySum += SIMTIME_DBL(failureLatency));
00311 
00312         }
00313         break;
00314     }
00315     RPC_ON_CALL(Lookup) {
00316         KbrRpcContext* kbrRpcContext = check_and_cast<KbrRpcContext*>(context);
00317         if (kbrRpcContext->getMeasurementPhase() == true) {
00318             RECORD_STATS(numLookupFailed++);
00319             // for failed lookups add failureLatency to latency statistics vector
00320             RECORD_STATS(globalStatistics->recordOutVector(
00321                          "KBRTestApp: Lookup Total Latency",
00322                          SIMTIME_DBL(failureLatency)));
00323         }
00324         break;
00325     }
00326     RPC_SWITCH_END()
00327 
00328     delete context;
00329 }
00330 
00331 void KBRTestApp::handleLookupResponse(LookupResponse* msg,
00332                                       cObject* context, simtime_t latency)
00333 {
00334     EV << "[KBRTestApp::handleLookupResponse() @ " << overlay->getThisNode().getIp()
00335        << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00336        << "    Lookup response for key " << msg->getKey()<< " : ";
00337 
00338     KbrRpcContext* kbrRpcContext = check_and_cast<KbrRpcContext*>(context);
00339 
00340     if (kbrRpcContext->getMeasurementPhase() == true) {
00341         if (msg->getIsValid() && (!lookupNodeIds ||
00342                 ((msg->getSiblingsArraySize() > 0) &&
00343                  (msg->getSiblings(0).getKey() == msg->getKey()) &&
00344                  (kbrRpcContext->getDestAddr() == msg->getSiblings(0))))) {
00345             RECORD_STATS(numLookupSuccess++);
00346             RECORD_STATS(globalStatistics->recordOutVector(
00347                    "KBRTestApp: Lookup Success Latency", SIMTIME_DBL(latency)));
00348             RECORD_STATS(globalStatistics->recordOutVector(
00349                    "KBRTestApp: Lookup Total Latency", SIMTIME_DBL(latency)));
00350             RECORD_STATS(globalStatistics->recordOutVector(
00351                     "KBRTestApp: Lookup Hop Count", msg->getHopCount()));
00352         } else {
00353 #if 0
00354             if (!msg->getIsValid()) {
00355                 std::cout << "invalid" << std::endl;
00356             } else if (msg->getSiblingsArraySize() == 0) {
00357                 std::cout << "empty" << std::endl;
00358             } else {
00359                 std::cout << "wrong key" << std::endl;
00360             }
00361 #endif
00362             RECORD_STATS(numLookupFailed++);
00363             // for failed lookups add failureLatency to latency statistics vector
00364             RECORD_STATS(globalStatistics->recordOutVector(
00365                          "KBRTestApp: Lookup Total Latency",
00366                          SIMTIME_DBL(failureLatency)));
00367             RECORD_STATS(globalStatistics->recordOutVector(
00368                     "KBRTestApp: Failed Lookup Hop Count", msg->getHopCount()));
00369         }
00370     }
00371 
00372     delete kbrRpcContext;
00373 }
00374 
00375 void KBRTestApp::handleNodeLeaveNotification()
00376 {
00377     nodeIsLeavingSoon = true;
00378 }
00379 
00380 void KBRTestApp::deliver(OverlayKey& key, cMessage* msg)
00381 {
00382     KBRTestMessage* testMsg = check_and_cast<KBRTestMessage*>(msg);
00383     OverlayCtrlInfo* overlayCtrlInfo =
00384         check_and_cast<OverlayCtrlInfo*>(msg->removeControlInfo());
00385 
00386     if (overlay->getThisNode().getKey().isUnspecified())
00387         error("key");
00388 
00389     // check for duplicate
00390     if ((msgHandleBufSize > 0 )
00391             && checkSeen(overlayCtrlInfo->getSrcNode().getKey(), testMsg->getSeqNum())) {
00392         EV << "[KBRTestApp::deliver() @ " << overlay->getThisNode().getIp()
00393            << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00394            << "    Duplicate dropped."
00395            << endl;
00396         delete overlayCtrlInfo;
00397         delete testMsg;
00398         return;
00399     }
00400 
00401     // Return statistical data to the sender.
00402     if (cModule* mod = simulation.getModule(testMsg->getId())) {
00403         if (KBRTestApp* sender = dynamic_cast<KBRTestApp*>(mod)) {
00404             if ((!lookupNodeIds) || (overlay->getThisNode().getKey() == key)) {
00405                 if (testMsg->getMeasurementPhase() == true) {
00406                         sender->evaluateData((simTime() - testMsg->getCreationTime()),
00407                                              overlayCtrlInfo->getHopCount(),
00408                                              testMsg->getByteLength());
00409                 }
00410             } else if(lookupNodeIds) {
00411                 if (testMsg->getMeasurementPhase() == true) {
00412                     RECORD_STATS(numDropped++;
00413                                  bytesDropped += testMsg->getByteLength());
00414                 }
00415                 EV << "[KBRTestApp::deliver() @ " << overlay->getThisNode().getIp()
00416                    << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00417                    << "    Error: Lookup of NodeIDs and KBRTestMessage"
00418                    << " received with different destKey!"
00419                    << endl;
00420             }
00421         }
00422     }
00423 
00424     EV << "[KBRTestApp::deliver() @ " << overlay->getThisNode().getIp()
00425        << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00426        << "    Received \"" << testMsg->getName() << "(seqNr: "
00427        << testMsg->getSeqNum() << ")\n"
00428        << "    with destination key: " << key.toString(16)
00429        << endl;
00430 
00431     delete overlayCtrlInfo;
00432     delete testMsg;
00433 }
00434 
00435 void KBRTestApp::forward(OverlayKey* key, cPacket** msg,
00436                          NodeHandle* nextHopNode)
00437 {
00438     KBRTestMessage* tempMsg = dynamic_cast<KBRTestMessage*>(*msg);
00439 
00440     if (tempMsg == NULL) return;
00441 
00442     tempMsg->setVisitedNodesArraySize(tempMsg->getVisitedNodesArraySize() + 1);
00443     tempMsg->setVisitedNodes(tempMsg->getVisitedNodesArraySize() - 1,
00444                              overlay->getThisNode().getIp());
00445 }
00446 
00447 std::pair<OverlayKey, TransportAddress> KBRTestApp::createDestKey()
00448 {
00449     if (lookupNodeIds) {
00450         const NodeHandle& handle = globalNodeList->getRandomNode(0, true,
00451                                                    onlyLookupInoffensiveNodes);
00452         return std::make_pair(handle.getKey(), handle);
00453     }
00454     // generate random destination key
00455     return std::make_pair(OverlayKey::random(), TransportAddress::UNSPECIFIED_NODE);
00456 }
00457 
00458 bool KBRTestApp::checkSeen(const OverlayKey& key, int seqNum)
00459 {
00460     MsgHandle hdl(key, seqNum);
00461 
00462     for (MsgHandleBuf::iterator it = mhBufBegin; it != mhBufEnd; ++it) {
00463         if (it->key.isUnspecified()) {
00464             continue;
00465         }
00466         if (*it == hdl) {
00467             return true;
00468         }
00469     }
00470 
00471     *(mhBufNext++) = hdl;
00472     if (mhBufNext == mhBufEnd) {
00473         mhBufNext = mhBufBegin;
00474     }
00475 
00476     return false;
00477 }
00478 
00479 void KBRTestApp::evaluateData(simtime_t latency, int hopCount, long int bytes)
00480 {
00481     // count the number and size of successfully delivered messages
00482     RECORD_STATS(numDelivered++; bytesDelivered += bytes;
00483                  globalStatistics->deliveredKBRTestAppMessages++);
00484 
00485     if (numSent < numDelivered) {
00486         std::ostringstream tempString;
00487         tempString << "KBRTestApp::evaluateData(): numSent ("
00488                    << numSent << ") < numDelivered (" << numDelivered << ")!";
00489         throw cRuntimeError(tempString.str().c_str());
00490     }
00491 
00492     RECORD_STATS(globalStatistics->recordOutVector("KBRTestApp: One-way Hop "
00493                                                    "Count", hopCount));
00494     RECORD_STATS(globalStatistics->recordOutVector("KBRTestApp: One-way Latency",
00495                                                    SIMTIME_DBL(latency)));
00496 }
00497 
00498 void KBRTestApp::finishApp()
00499 {
00500     simtime_t time = globalStatistics->calcMeasuredLifetime(creationTime);
00501 
00502     if (time >= GlobalStatistics::MIN_MEASURED) {
00503         if (kbrOneWayTest) {
00504             globalStatistics->addStdDev("KBRTestApp: One-way Delivered Messages/s",
00505                                         numDelivered / time);
00506             globalStatistics->addStdDev("KBRTestApp: One-way Delivered Bytes/s",
00507                                         bytesDelivered / time);
00508             globalStatistics->addStdDev("KBRTestApp: One-way Dropped Messages/s",
00509                                         numDropped / time);
00510             globalStatistics->addStdDev("KBRTestApp: One-way Dropped Bytes/s",
00511                                         bytesDropped / time);
00512             if (numSent > 0) {
00513                 globalStatistics->addStdDev("KBRTestApp: One-way Delivery Ratio",
00514                                             (float)numDelivered /
00515                                             (float)numSent);
00516             }
00517         }
00518 
00519         if (kbrRpcTest) {
00520             globalStatistics->addStdDev("KBRTestApp: RPC Delivered Messages/s",
00521                                         numRpcDelivered / time);
00522             globalStatistics->addStdDev("KBRTestApp: RPC Delivered Bytes/s",
00523                                         bytesRpcDelivered / time);
00524             globalStatistics->addStdDev("KBRTestApp: RPC Dropped Messages/s",
00525                                         numRpcDropped / time);
00526             globalStatistics->addStdDev("KBRTestApp: RPC Dropped Bytes/s",
00527                                         bytesRpcDropped / time);
00528 #if 0
00529             if (rpcSuccLatencyCount > 0) {
00530                 globalStatistics->addStdDev("KBRTestApp: RPC Success Session Latency",
00531                                         SIMTIME_DBL(rpcSuccLatencySum) / rpcSuccLatencyCount);
00532             }
00533 
00534             if (rpcTotalLatencyCount > 0) {
00535                 globalStatistics->addStdDev("KBRTestApp: RPC Total Session Latency",
00536                                         SIMTIME_DBL(rpcTotalLatencySum) / rpcTotalLatencyCount);
00537             }
00538 #endif
00539 
00540             if (numRpcSent > 0) {
00541                 globalStatistics->addStdDev("KBRTestApp: RPC Delivery Ratio",
00542                                             (float)numRpcDelivered /
00543                                             (float)numRpcSent);
00544             }
00545         }
00546 
00547         if (kbrLookupTest) {
00548             globalStatistics->addStdDev("KBRTestApp: Successful Lookups/s",
00549                                         numLookupSuccess / time);
00550             globalStatistics->addStdDev("KBRTestApp: Failed Lookups/s",
00551                                         numLookupFailed / time);
00552             if (numLookupSent > 0) {
00553                 globalStatistics->addStdDev("KBRTestApp: Lookup Success Ratio",
00554                                             (float)numLookupSuccess /
00555                                             (float)numLookupSent);
00556             }
00557         }
00558     }
00559 }