RealtimeScheduler Class Reference

#include <realtimescheduler.h>

Inheritance diagram for RealtimeScheduler:

TunOutScheduler UdpOutScheduler

List of all members.


Detailed Description

This class implements a event scheduler for OMNeT++ It makes the simulation run in realtime (i.e.

1 simsec == 1 sec) It must be subclassed; its subclasses must handle network traffic from/to the simulation

Public Types

typedef std::list
< PacketBufferEntry
PacketBuffer

Public Member Functions

 RealtimeScheduler ()
 Constructor.
virtual ~RealtimeScheduler ()
 Destructor.
virtual void startRun ()
 Called at the beginning of a simulation run.
virtual void endRun ()
 Called at the end of a simulation run.
virtual void executionResumed ()
 Recalculates "base time" from current wall clock time.
virtual void setInterfaceModule (cModule *module, cMessage *notificationMsg, PacketBuffer *buffer, int mtu, bool isApp=false)
 To be called from the module which wishes to receive data from the tun device.
virtual cMessage * getNextEvent ()
 Scheduler function -- it comes from cScheduler interface.
void sendNotificationMsg (cMessage *msg, cModule *mod)
 send notification msg to module
virtual ssize_t sendBytes (const char *buf, size_t numBytes, sockaddr *addr=0, socklen_t addrlen=0, bool isApp=false, int fd=-1)
 Send data to network.
void closeAppSocket (int fd)
 Close the application TCP socket.

Protected Member Functions

virtual int initializeNetwork ()=0
 Initialize the network.
virtual void additionalFD ()
 This function is called from main loop if data is accessible from "additional_fd".
virtual bool receiveWithTimeout (long usec)
 Waits for incoming data on the tun device.
virtual int receiveUntil (const timeval &targetTime)
 Tries to read data until the given time is up.

Protected Attributes

fd_set all_fds
int maxfd
int netw_fd
cModule * module
cMessage * notificationMsg
PacketBufferpacketBuffer
size_t buffersize
cModule * appModule
cMessage * appNotificationMsg
PacketBufferappPacketBuffer
size_t appBuffersize
int appConnectionLimit
int additional_fd
timeval baseTime

Classes

class  PacketBufferEntry


Member Typedef Documentation


Constructor & Destructor Documentation

RealtimeScheduler::RealtimeScheduler (  ) 

Constructor.

00033                                      : cScheduler()
00034 {
00035     FD_ZERO(&all_fds);
00036     maxfd = -1;
00037     netw_fd = -1;
00038     additional_fd = -1;
00039 }

RealtimeScheduler::~RealtimeScheduler (  )  [virtual]

Destructor.

00042 { }


Member Function Documentation

virtual int RealtimeScheduler::initializeNetwork (  )  [protected, pure virtual]

Initialize the network.

Implemented in TunOutScheduler, and UdpOutScheduler.

Referenced by startRun().

virtual void RealtimeScheduler::additionalFD (  )  [inline, protected, virtual]

This function is called from main loop if data is accessible from "additional_fd".

This FD can be set in initializeNetwork by concrete implementations.

Reimplemented in TunOutScheduler, and UdpOutScheduler.

Referenced by receiveWithTimeout().

00095 {};

bool RealtimeScheduler::receiveWithTimeout ( long  usec  )  [protected, virtual]

Waits for incoming data on the tun device.

Parameters:
usec Timeout after which to quit waiting (in µsec)
Returns:
true if data was read, false otherwise

Referenced by receiveUntil().

00103 {
00104     bool newEvent = false;
00105     // prepare sets for select()
00106     fd_set readFD;
00107     readFD = all_fds;
00108 
00109     timeval timeout;
00110     timeout.tv_sec = 0;
00111     timeout.tv_usec = usec;
00112 
00113     if (select(FD_SETSIZE, &readFD, NULL, NULL, &timeout) > 0) {
00114         // Read on all sockets with data
00115         for( int fd = 0; fd <= maxfd; fd++) {
00116             if( FD_ISSET(fd, &readFD)) {
00117                 // Incoming data on netw_fd
00118                 if( fd == netw_fd ) {
00119                     char* buf = new char[buffersize];
00120                     int nBytes;
00121 
00122                     // FIXME: Ugly. But we want to support IPv4 and IPv6 here, so we
00123                     // reserve enough space for the "bigger" address.
00124                     sockaddr* from = (sockaddr*) new sockaddr_in6;
00125                     socklen_t addrlen = sizeof(sockaddr_in6);
00126 
00127                     // FIXME: Ugly...
00128                     getsockname(netw_fd, from, &addrlen);
00129                     if ( from->sa_family != SOCK_DGRAM ) {
00130                         delete from;
00131                         from = 0;
00132                         addrlen = 0;
00133                         nBytes = read(netw_fd, buf, buffersize);
00134                     } else {
00135                         addrlen = sizeof(sockaddr_in6);
00136                         nBytes = recvfrom(netw_fd, buf, buffersize, 0, from, &addrlen);
00137                     }
00138 
00139                     if (nBytes < 0) {
00140                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00141                             << "    Error reading from network: " << strerror(errno)
00142                             << endl;
00143                         opp_error("Read from network device returned an error");
00144                     } else if (nBytes == 0) {
00145                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00146                            << "    Received 0 byte long UDP packet!" << endl;
00147                     } else {
00148                         // write data to buffer
00149                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00150                             << "    Received " << nBytes << " bytes"
00151                             << endl;
00152                         packetBuffer->push_back(PacketBufferEntry(buf, nBytes, from, addrlen));
00153                         // schedule notificationMsg for the interface module
00154                         sendNotificationMsg(notificationMsg, module);
00155                         newEvent = true;
00156                     }
00157                 } else if ( fd == additional_fd ) {
00158                     // Data on additional FD
00159                     additionalFD();
00160                     newEvent = true;
00161                 } else {
00162                     // Data on app FD
00163                     char* buf = new char[appBuffersize];
00164                     int nBytes = read(fd, buf, appBuffersize);
00165                     if (nBytes < 0) {
00166                         opp_error("Read from network device returned an error (App)");
00167                     } else if (nBytes == 0) {
00168                         // Application closed Socket
00169                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00170                             << "    Application closed socket"
00171                             << endl;
00172 
00173                         closeAppSocket(fd);
00174                         newEvent = true;
00175                     } else {
00176                         // write data to buffer
00177                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00178                             << "    Received " << nBytes << " bytes"
00179                             << endl;
00180                         appPacketBuffer->push_back(PacketBufferEntry(buf, nBytes, PacketBufferEntry::DATA, fd));
00181                         // schedule notificationMsg for the interface module
00182                         sendNotificationMsg(appNotificationMsg, appModule);
00183                         newEvent = true;
00184                     }
00185                 }
00186             }
00187         }
00188     }
00189     return newEvent;
00190 }

int RealtimeScheduler::receiveUntil ( const timeval &  targetTime  )  [protected, virtual]

Tries to read data until the given time is up.

Parameters:
targetTime stop waiting after this time is up
Returns:
1 if data is read, -1 if there is an error, 0 if no data is read

Referenced by getNextEvent().

00193 {
00194     // if there's more than 200ms to wait, wait in 100ms chunks
00195     // in order to keep UI responsiveness by invoking ev.idle()
00196     timeval curTime;
00197     gettimeofday(&curTime, NULL);
00198     while (targetTime.tv_sec-curTime.tv_sec >=2 ||
00199             timeval_diff_usec(targetTime, curTime) >= 200000) {
00200         if (receiveWithTimeout(100000)) { // 100ms
00201             if (ev.idle()) return -1;
00202             return 1;
00203         }
00204         if (ev.idle()) return -1;
00205         gettimeofday(&curTime, NULL);
00206     }
00207 
00208     // difference is now at most 100ms, do it at once
00209     long usec = timeval_diff_usec(targetTime, curTime);
00210     if (usec>0)
00211         if (receiveWithTimeout(usec)) {
00212             if (ev.idle()) return -1;
00213             return 1;
00214         }
00215     if (ev.idle()) return -1;
00216     return 0;
00217 }

void RealtimeScheduler::startRun (  )  [virtual]

Called at the beginning of a simulation run.

00045 {
00046     if (initsocketlibonce()!=0)
00047         throw new cRuntimeError("RealtimeScheduler: Cannot initialize socket library");
00048 
00049     gettimeofday(&baseTime, NULL);
00050 
00051     appModule = NULL;
00052     appNotificationMsg = NULL;
00053     module = NULL;
00054     notificationMsg = NULL;
00055 
00056     appConnectionLimit = ev.config()->getAsInt("ExternalApp", "appConnectionLimit");
00057 
00058     if (initializeNetwork()) {
00059         opp_error("realtimeScheduler error: initializeNetwork failed\n");
00060     }
00061 }

void RealtimeScheduler::endRun (  )  [virtual]

Called at the end of a simulation run.

00064 {}

void RealtimeScheduler::executionResumed (  )  [virtual]

Recalculates "base time" from current wall clock time.

00067 {
00068     gettimeofday(&baseTime, NULL);
00069     baseTime = timeval_substract(baseTime, sim->simTime());
00070 }

void RealtimeScheduler::setInterfaceModule ( cModule *  module,
cMessage *  notificationMsg,
PacketBuffer buffer,
int  mtu,
bool  isApp = false 
) [virtual]

To be called from the module which wishes to receive data from the tun device.

The method must be called from the module's initialize() function.

Parameters:
module Pointer to the module that wants to receive the data
notificationMsg A pointer to a message that will be scheduled if there is data to read
buffer A pointer to the buffer the data will be written into
mtu Max allowed packet size
isApp set to "true" if called from a realworldApp

Referenced by RealworldConnector::initialize(), and XmlRpcInterface::initializeApp().

00075 {
00076     if (!mod || !notifMsg || !buffer) {
00077         throw new cRuntimeError("RealtimeScheduler: setInterfaceModule(): "
00078                                 "arguments must be non-NULL");
00079     }
00080 
00081     if (!isApp) {
00082         if (module) {
00083             throw new cRuntimeError("RealtimeScheduler: setInterfaceModule() "
00084                                     "already called");
00085         }
00086         module = mod;
00087         notificationMsg = notifMsg;
00088         packetBuffer = buffer;
00089         buffersize = mtu;
00090     } else {
00091         if (appModule) {
00092             throw new cRuntimeError("RealtimeScheduler: setInterfaceModule() "
00093                                     "already called");
00094         }
00095         appModule = mod;
00096         appNotificationMsg = notifMsg;
00097         appPacketBuffer = buffer;
00098         appBuffersize = mtu;
00099     }
00100 }

cMessage * RealtimeScheduler::getNextEvent (  )  [virtual]

Scheduler function -- it comes from cScheduler interface.

00220 {
00221     // assert that we've been configured
00222     if (!module)
00223         throw new cRuntimeError("RealtimeScheduler: setInterfaceModule() not called: it must be called from a module's initialize() function");
00224     // FIXME: reimplement sanity check
00225 //    if (app_fd >= 0 && !appModule)
00226 //        throw new cRuntimeError("RealtimeScheduler: setInterfaceModule() not called from application: it must be called from a module's initialize() function");
00227 
00228     // calculate target time
00229     timeval targetTime;
00230     cMessage *msg = sim->msgQueue.peekFirst();
00231     if (!msg) {
00232         // if there are no events, wait until something comes from outside
00233         // TBD: obey simtimelimit, cpu-time-limit
00234         targetTime.tv_sec = LONG_MAX;
00235         targetTime.tv_usec = 0;
00236     } else {
00237         // use time of next event
00238         simtime_t eventSimtime = msg->arrivalTime();
00239         targetTime = timeval_add(baseTime, eventSimtime);
00240     }
00241 
00242     // if needed, wait until that time arrives
00243     timeval curTime;
00244     gettimeofday(&curTime, NULL);
00245     if (timeval_greater(targetTime, curTime)) {
00246         int status = receiveUntil(targetTime);
00247         if (status == -1) {
00248             printf("WARNING: receiveUntil returned -1 (user interrupt)\n");
00249             return NULL; // interrupted by user
00250         } else if (status == 1) {
00251             msg = sim->msgQueue.peekFirst(); // received something
00252         }
00253     } else {
00254         //    printf("WARNING: Lagging behind realtime!\n");
00255         // we're behind -- customized versions of this class may
00256         // alert if we're too much behind, whatever that means
00257     }
00258     // ok, return the message
00259     return msg;
00260 }

void RealtimeScheduler::sendNotificationMsg ( cMessage *  msg,
cModule *  mod 
)

send notification msg to module

Parameters:
msg The notification Message
mod The destination

Referenced by UdpOutScheduler::additionalFD(), TunOutScheduler::additionalFD(), closeAppSocket(), and receiveWithTimeout().

00272 {
00273     if( msg->isScheduled() ) return; // Notification already scheduled
00274     timeval curTime;
00275     gettimeofday(&curTime, NULL);
00276     curTime = timeval_substract(curTime, baseTime);
00277     simtime_t t = curTime.tv_sec + curTime.tv_usec*1e-6;
00278 
00279     msg->setSentFrom(mod, -1, simulation.simTime());
00280     msg->setArrival(mod,-1,t);
00281     simulation.msgQueue.insert(msg);
00282 }

ssize_t RealtimeScheduler::sendBytes ( const char *  buf,
size_t  numBytes,
sockaddr *  addr = 0,
socklen_t  addrlen = 0,
bool  isApp = false,
int  fd = -1 
) [virtual]

Send data to network.

Parameters:
buf A pointer to the data to be send
numBytes the length of the data
isApp set to "true" if called from a realworldApp
addr If needed, the destination address
addrlen The length of the address
fd If connected to more than one external app, set to the corresponding FD. If left to default and multiple apps are connected, the data will be send to one arbitrarily chosen app.
Returns:
The number of bytes written, -1 on error

Referenced by SimpleGameClient::handleLowerMessage(), SimpleGameClient::handleRealworldPacket(), SimpleGameClient::handleTimerEvent(), RealworldConnector::transmitToNetwork(), SimpleGameClient::updateNeighbors(), and XmlRpcInterface::writeResponse().

00290 {
00291     if (!buf) {
00292         ev << "[RealtimeScheduler::sendBytes()]\n"
00293         << "    Error sending packet: buf = NULL"
00294         << endl;
00295         return -1;
00296     }
00297     if (!isApp) {
00298         if( numBytes > buffersize ) {
00299             ev << "[RealtimeScheduler::sendBytes()]\n"
00300             << "    Trying to send oversized packet: size " << numBytes << " mtu " << buffersize
00301             << endl;
00302             opp_error("Can't send packet: too large"); //FIXME: Throw exception instead
00303         }
00304 
00305         if ( netw_fd < 0 ) {
00306             ev << "[RealtimeScheduler::sendBytes()]\n"
00307             << "    Can't send packet to network: no tun socket"
00308             << endl;
00309             return 0;
00310         }
00311         int nBytes;
00312         if (addr) {
00313             nBytes =  sendto(netw_fd, buf, numBytes, 0, addr, addrlen);
00314         } else {
00315             nBytes =  write(netw_fd, buf, numBytes);
00316         }
00317         if (nBytes < 0) {
00318             ev << "[RealtimeScheduler::sendBytes()]\n"
00319             << "    Error sending data to network: " << strerror(errno) << "\n"
00320             << "    FD = " << netw_fd << ", numBytes = " << numBytes <<  ", addrlen = " << addrlen
00321             << endl;
00322         }
00323         return nBytes;
00324 
00325     } else {
00326         if( numBytes > appBuffersize ) {
00327             ev << "[RealtimeScheduler::sendBytes()]\n"
00328             << "    Trying to send oversized packet: size " << numBytes << "\n"
00329             << "    mtu " << appBuffersize
00330             << endl;
00331             opp_error("Can't send packet: too large"); //FIXME: Throw exception instead
00332         }
00333         // If no fd is given, select a "random" one
00334         if ( fd < 0 ) {
00335             for ( fd = 0; fd <= maxfd; fd++ ) {
00336                 if( fd == netw_fd ) continue;
00337                 if( fd == additional_fd ) continue;
00338                 if( FD_ISSET(fd, &all_fds)) break;
00339             }
00340             if ( fd > maxfd ) throw new cException("Can't send packet to Application: no socket");
00341         }
00342         return write(fd, buf, numBytes);
00343     }
00344     // TBD check for errors
00345 }

void RealtimeScheduler::closeAppSocket ( int  fd  ) 

Close the application TCP socket.

Referenced by XmlRpcInterface::closeConnection(), and receiveWithTimeout().

00263 {
00264     close(fd);
00265     FD_CLR(fd, &all_fds);
00266 
00267     appPacketBuffer->push_back(PacketBufferEntry(0, 0, PacketBufferEntry::FD_CLOSE, fd));
00268     sendNotificationMsg(appNotificationMsg, appModule);
00269 }


Member Data Documentation

fd_set RealtimeScheduler::all_fds [protected]

int RealtimeScheduler::maxfd [protected]

int RealtimeScheduler::netw_fd [protected]

cModule* RealtimeScheduler::module [protected]

cMessage* RealtimeScheduler::notificationMsg [protected]

size_t RealtimeScheduler::buffersize [protected]

cModule* RealtimeScheduler::appModule [protected]

timeval RealtimeScheduler::baseTime [protected]


The documentation for this class was generated from the following files:

Generated on Fri Sep 19 13:05:08 2008 for ITM OverSim by  doxygen 1.5.5