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 accessable 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

typedef std::list<PacketBufferEntry> RealtimeScheduler::PacketBuffer


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.

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

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

This FD can be set in initializeNetwork by concrete implementations.

Reimplemented in TunOutScheduler, and UdpOutScheduler.

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

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

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", "connectionLimit");
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 whishes to receive the data
notificationMsg A pointer to a message that will be sheduled 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
00073 {
00074     if (!mod || !notifMsg || !buffer)
00075         throw new cRuntimeError("RealtimeScheduler: setInterfaceModule(): arguments must be non-NULL");
00076 
00077     if (!isApp) {
00078         if (module)
00079             throw new cRuntimeError("RealtimeScheduler: setInterfaceModule() already called");
00080 
00081         module = mod;
00082         notificationMsg = notifMsg;
00083         packetBuffer = buffer;
00084         buffersize = mtu;
00085     } else {
00086         if (appModule)
00087             throw new cRuntimeError("RealtimeScheduler: setInterfaceModule() already called");
00088 
00089         appModule = mod;
00090         std::cout << "RealtimeScheduler::setInferfaceModule: " << appModule->fullPath() << std::endl;
00091         appNotificationMsg = notifMsg;
00092         appPacketBuffer = buffer;
00093         appBuffersize = mtu;
00094     }
00095 }

cMessage * RealtimeScheduler::getNextEvent (  )  [virtual]

Scheduler function -- it comes from cScheduler interface.

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

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

send notification msg to module

Parameters:
msg The notification Message
mod The destination
00266 {
00267     if( msg->isScheduled() ) return; // Notification already scheduled
00268     timeval curTime;
00269     gettimeofday(&curTime, NULL);
00270     curTime = timeval_substract(curTime, baseTime);
00271     simtime_t t = curTime.tv_sec + curTime.tv_usec*1e-6;
00272     
00273     msg->setSentFrom(mod, -1, simulation.simTime());
00274     msg->setArrival(mod,-1,t);
00275     simulation.msgQueue.insert(msg);
00276 }

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

void RealtimeScheduler::closeAppSocket ( int  fd  ) 

Close the application TCP socket.

00257 {
00258     close(fd);
00259     FD_CLR(fd, &all_fds);
00260 
00261     appPacketBuffer->push_back(PacketBufferEntry(0, 0, PacketBufferEntry::FD_CLOSE, fd));
00262     sendNotificationMsg(appNotificationMsg, appModule);
00263 }


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]

PacketBuffer* RealtimeScheduler::packetBuffer [protected]

size_t RealtimeScheduler::buffersize [protected]

cModule* RealtimeScheduler::appModule [protected]

cMessage* RealtimeScheduler::appNotificationMsg [protected]

PacketBuffer* RealtimeScheduler::appPacketBuffer [protected]

size_t RealtimeScheduler::appBuffersize [protected]

int RealtimeScheduler::appConnectionLimit [protected]

int RealtimeScheduler::additional_fd [protected]

timeval RealtimeScheduler::baseTime [protected]


The documentation for this class was generated from the following files:
Generated on Thu Apr 17 13:19:30 2008 for ITM OverSim by  doxygen 1.5.3