RealtimeScheduler Class Reference

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

#include <realtimescheduler.h>

Inheritance diagram for RealtimeScheduler:
AppTunOutScheduler TunOutScheduler UdpOutScheduler

List of all members.

Classes

class  PacketBufferEntry

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, SOCKET fd=INVALID_SOCKET)
 Send data to network.
void closeAppSocket (SOCKET fd)
 Close the application TCP socket.
virtual SOCKET getAppTunFd ()
 Returns the FD for the application TUN 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
SOCKET maxfd
SOCKET netw_fd
SOCKET apptun_fd
cModule * module
cMessage * notificationMsg
PacketBufferpacketBuffer
size_t buffersize
cModule * appModule
cMessage * appNotificationMsg
PacketBufferappPacketBuffer
size_t appBuffersize
int appConnectionLimit
SOCKET additional_fd
timeval baseTime

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

Definition at line 38 of file realtimescheduler.h.


Member Typedef Documentation

Definition at line 64 of file realtimescheduler.h.


Constructor & Destructor Documentation

RealtimeScheduler::RealtimeScheduler (  ) 

Constructor.

Definition at line 34 of file realtimescheduler.cc.

00034                                      : cScheduler()
00035 {
00036     FD_ZERO(&all_fds);
00037     maxfd = 0;
00038     netw_fd = INVALID_SOCKET;
00039     additional_fd = INVALID_SOCKET;
00040     apptun_fd = INVALID_SOCKET;
00041 }

RealtimeScheduler::~RealtimeScheduler (  )  [virtual]

Destructor.

Definition at line 43 of file realtimescheduler.cc.

00044 { }


Member Function Documentation

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 AppTunOutScheduler, TunOutScheduler, and UdpOutScheduler.

Definition at line 99 of file realtimescheduler.h.

Referenced by receiveWithTimeout().

00099 {};

void RealtimeScheduler::closeAppSocket ( SOCKET  fd  ) 

Close the application TCP socket.

Definition at line 308 of file realtimescheduler.cc.

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

00309 {
00310 #ifdef _WIN32
00311     closesocket(fd);
00312 #else
00313     close(fd);
00314 #endif
00315     FD_CLR(fd, &all_fds);
00316 
00317     appPacketBuffer->push_back(PacketBufferEntry(0, 0, PacketBufferEntry::PACKET_FD_CLOSE, fd));
00318     sendNotificationMsg(appNotificationMsg, appModule);
00319 }

void RealtimeScheduler::endRun (  )  [virtual]

Called at the end of a simulation run.

Definition at line 65 of file realtimescheduler.cc.

00066 {}

void RealtimeScheduler::executionResumed (  )  [virtual]

Recalculates "base time" from current wall clock time.

Definition at line 68 of file realtimescheduler.cc.

00069 {
00070     gettimeofday(&baseTime, NULL);
00071     baseTime = timeval_substract(baseTime, SIMTIME_DBL(simTime()));
00072 }

virtual SOCKET RealtimeScheduler::getAppTunFd (  )  [inline, virtual]

Returns the FD for the application TUN socket.

Returns:
the application TUN socket FD

Definition at line 203 of file realtimescheduler.h.

Referenced by XmlRpcInterface::initializeApp().

00203 { return apptun_fd; };

cMessage * RealtimeScheduler::getNextEvent (  )  [virtual]

Scheduler function -- it comes from cScheduler interface.

Definition at line 265 of file realtimescheduler.cc.

00266 {
00267     // assert that we've been configured
00268     if (!module)
00269         throw cRuntimeError("RealtimeScheduler: setInterfaceModule() not called: it must be called from a module's initialize() function");
00270     // FIXME: reimplement sanity check
00271 //    if (app_fd >= 0 && !appModule)
00272 //        throw cRuntimeError("RealtimeScheduler: setInterfaceModule() not called from application: it must be called from a module's initialize() function");
00273 
00274     // calculate target time
00275     timeval targetTime;
00276     cMessage *msg = sim->msgQueue.peekFirst();
00277     if (!msg) {
00278         // if there are no events, wait until something comes from outside
00279         // TBD: obey simtimelimit, cpu-time-limit
00280         targetTime.tv_sec = LONG_MAX;
00281         targetTime.tv_usec = 0;
00282     } else {
00283         // use time of next event
00284         simtime_t eventSimtime = msg->getArrivalTime();
00285         targetTime = timeval_add(baseTime, SIMTIME_DBL(eventSimtime));
00286     }
00287 
00288     // if needed, wait until that time arrives
00289     timeval curTime;
00290     gettimeofday(&curTime, NULL);
00291     if (timeval_greater(targetTime, curTime)) {
00292         int status = receiveUntil(targetTime);
00293         if (status == -1) {
00294             printf("WARNING: receiveUntil returned -1 (user interrupt)\n");
00295             return NULL; // interrupted by user
00296         } else if (status == 1) {
00297             msg = sim->msgQueue.peekFirst(); // received something
00298         }
00299     } else {
00300         //    printf("WARNING: Lagging behind realtime!\n");
00301         // we're behind -- customized versions of this class may
00302         // alert if we're too much behind, whatever that means
00303     }
00304     // ok, return the message
00305     return msg;
00306 }

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

Initialize the network.

Implemented in AppTunOutScheduler, TunOutScheduler, and UdpOutScheduler.

Referenced by startRun().

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

Definition at line 238 of file realtimescheduler.cc.

Referenced by getNextEvent().

00239 {
00240     // if there's more than 200ms to wait, wait in 100ms chunks
00241     // in order to keep UI responsiveness by invoking ev.idle()
00242     timeval curTime;
00243     gettimeofday(&curTime, NULL);
00244     while (targetTime.tv_sec-curTime.tv_sec >=2 ||
00245             timeval_diff_usec(targetTime, curTime) >= 200000) {
00246         if (receiveWithTimeout(100000)) { // 100ms
00247             if (ev.idle()) return -1;
00248             return 1;
00249         }
00250         if (ev.idle()) return -1;
00251         gettimeofday(&curTime, NULL);
00252     }
00253 
00254     // difference is now at most 100ms, do it at once
00255     long usec = timeval_diff_usec(targetTime, curTime);
00256     if (usec>0)
00257         if (receiveWithTimeout(usec)) {
00258             if (ev.idle()) return -1;
00259             return 1;
00260         }
00261     if (ev.idle()) return -1;
00262     return 0;
00263 }

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

Definition at line 104 of file realtimescheduler.cc.

Referenced by receiveUntil().

00105 {
00106     bool newEvent = false;
00107     // prepare sets for select()
00108     fd_set readFD;
00109     readFD = all_fds;
00110 
00111     timeval timeout;
00112     timeout.tv_sec = 0;
00113     timeout.tv_usec = usec;
00114 
00115     if (select(FD_SETSIZE, &readFD, NULL, NULL, &timeout) > 0) {
00116         // Read on all sockets with data
00117         for (SOCKET fd = 0; fd <= maxfd; fd++) {
00118             if (FD_ISSET(fd, &readFD)) {
00119                 // Incoming data on netw_fd
00120                 if (fd == netw_fd) {
00121                     char* buf = new char[buffersize];
00122                     int nBytes;
00123 
00124                     // FIXME: Ugly. But we want to support IPv4 and IPv6 here, so we
00125                     // reserve enough space for the "bigger" address.
00126                     sockaddr* from = (sockaddr*) new sockaddr_in;
00127                     socklen_t addrlen = sizeof(sockaddr_in);
00128 
00129                     // FIXME: Ugly...
00130                     getsockname(netw_fd, from, &addrlen);
00131                     if ( from->sa_family != SOCK_DGRAM ) {
00132                         delete from;
00133                         from = 0;
00134                         addrlen = 0;
00135                         // use read() for TUN device
00136                         nBytes = read(netw_fd, buf, buffersize);
00137                     } else {
00138                         addrlen = sizeof(sockaddr_in);
00139                         nBytes = recvfrom(netw_fd, buf, buffersize, 0, from, &addrlen);
00140                     }
00141 
00142                     if (nBytes < 0) {
00143                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00144                             << "    Error reading from network: " << strerror(sock_errno())
00145                             << endl;
00146                         delete[] buf;
00147                         buf = NULL;
00148                         opp_error("Read from network device returned an error");
00149                     } else if (nBytes == 0) {
00150                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00151                            << "    Received 0 byte long UDP packet!" << endl;
00152                         delete[] buf;
00153                         buf = NULL;
00154                     } else {
00155                         // write data to buffer
00156                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00157                             << "    Received " << nBytes << " bytes"
00158                             << endl;
00159                         packetBuffer->push_back(PacketBufferEntry(buf, nBytes, from, addrlen));
00160                         // schedule notificationMsg for the interface module
00161                         sendNotificationMsg(notificationMsg, module);
00162                         newEvent = true;
00163                     }
00164                 } else if ( fd == apptun_fd ) {
00165                     // Data on application TUN FD
00166                     char* buf = new char[appBuffersize];
00167                     // use read() for TUN device
00168                     int nBytes = read(fd, buf, appBuffersize);
00169 
00170                     if (nBytes < 0) {
00171                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00172                             << "    Error reading from application TUN socket: "
00173                             << strerror(sock_errno())
00174                             << endl;
00175                         delete[] buf;
00176                         buf = NULL;
00177                         opp_error("Read from application TUN socket returned "
00178                                   "an error");
00179                     } else if (nBytes == 0) {
00180                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00181                            << "    Received 0 byte long UDP packet!" << endl;
00182                         delete[] buf;
00183                         buf = NULL;
00184                     } else {
00185                         // write data to buffer
00186                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00187                             << "    Received " << nBytes << " bytes"
00188                             << endl;
00189 
00190                         appPacketBuffer->push_back(PacketBufferEntry(buf,
00191                             nBytes, PacketBufferEntry::PACKET_APPTUN_DATA, fd));
00192 
00193                         // schedule notificationMsg for the interface module
00194                         sendNotificationMsg(appNotificationMsg, appModule);
00195                         newEvent = true;
00196                     }
00197                 } else if ( fd == additional_fd ) {
00198                     // Data on additional FD
00199                     additionalFD();
00200                     newEvent = true;
00201                 } else {
00202                     // Data on app FD
00203                     char* buf = new char[appBuffersize];
00204                     int nBytes = recv(fd, buf, appBuffersize, 0);
00205                     if (nBytes < 0) {
00206                         delete[] buf;
00207                         buf = NULL;
00208                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00209                             << "    Read error from application socket: "
00210                             << strerror(sock_errno()) << endl;
00211                         opp_error("Read from network device returned an error (App)");
00212                     } else if (nBytes == 0) {
00213                         // Application closed Socket
00214                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00215                             << "    Application closed socket"
00216                             << endl;
00217                         delete[] buf;
00218                         buf = NULL;
00219                         closeAppSocket(fd);
00220                         newEvent = true;
00221                     } else {
00222                         // write data to buffer
00223                         ev << "[RealtimeScheduler::receiveWithTimeout()]\n"
00224                             << "    Received " << nBytes << " bytes"
00225                             << endl;
00226                         appPacketBuffer->push_back(PacketBufferEntry(buf, nBytes, PacketBufferEntry::PACKET_DATA, fd));
00227                         // schedule notificationMsg for the interface module
00228                         sendNotificationMsg(appNotificationMsg, appModule);
00229                         newEvent = true;
00230                     }
00231                 }
00232             }
00233         }
00234     }
00235     return newEvent;
00236 }

ssize_t RealtimeScheduler::sendBytes ( const char *  buf,
size_t  numBytes,
sockaddr *  addr = 0,
socklen_t  addrlen = 0,
bool  isApp = false,
SOCKET  fd = INVALID_SOCKET 
) [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

Definition at line 338 of file realtimescheduler.cc.

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

00344 {
00345     if (!buf) {
00346         ev << "[RealtimeScheduler::sendBytes()]\n"
00347         << "    Error sending packet: buf = NULL"
00348         << endl;
00349         return -1;
00350     }
00351     if (!isApp) {
00352         if( numBytes > buffersize ) {
00353             ev << "[RealtimeScheduler::sendBytes()]\n"
00354             << "    Trying to send oversized packet: size " << numBytes << " mtu " << buffersize
00355             << endl;
00356             opp_error("Can't send packet: too large"); //FIXME: Throw exception instead
00357         }
00358 
00359         if ( netw_fd == INVALID_SOCKET ) {
00360             ev << "[RealtimeScheduler::sendBytes()]\n"
00361             << "    Can't send packet to network: no tun/udp socket"
00362             << endl;
00363             return 0;
00364         }
00365         int nBytes;
00366         if (addr) {
00367             nBytes =  sendto(netw_fd, buf, numBytes, 0, addr, addrlen);
00368         } else {
00369             // TUN
00370             nBytes =  write(netw_fd, buf, numBytes);
00371         }
00372         if (nBytes < 0) {
00373             ev << "[RealtimeScheduler::sendBytes()]\n"
00374             << "    Error sending data to network: " << strerror(sock_errno()) << "\n"
00375             << "    FD = " << netw_fd << ", numBytes = " << numBytes <<  ", addrlen = " << addrlen
00376             << endl;
00377         }
00378         return nBytes;
00379 
00380     } else {
00381         if (numBytes > appBuffersize) {
00382             ev << "[RealtimeScheduler::sendBytes()]\n"
00383             << "    Trying to send oversized packet: size " << numBytes << "\n"
00384             << "    mtu " << appBuffersize
00385             << endl;
00386             opp_error("Can't send packet: too large"); //FIXME: Throw exception instead
00387         }
00388         // If no fd is given, select a "random" one
00389         if (fd == INVALID_SOCKET) {
00390             for (fd = 0; fd <= maxfd; fd++) {
00391                 if (fd == netw_fd) continue;
00392                 if (fd == additional_fd) continue;
00393                 if (FD_ISSET(fd, &all_fds)) break;
00394             }
00395             if (fd > maxfd) {
00396                 throw cRuntimeError("Can't send packet to Application: no socket");
00397             }
00398         }
00399         if (fd == apptun_fd) {
00400             // Application TUN FD
00401             return write(fd, buf, numBytes);
00402         } else {
00403             return send(fd, buf, numBytes, 0);
00404         }
00405     }
00406     // TBD check for errors
00407 }

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

send notification msg to module

Parameters:
msg The notification Message
mod The destination

Definition at line 321 of file realtimescheduler.cc.

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

00322 {
00323     if (msg->isScheduled()) return; // Notification already scheduled
00324     timeval curTime;
00325     gettimeofday(&curTime, NULL);
00326     curTime = timeval_substract(curTime, baseTime);
00327     simtime_t t = curTime.tv_sec + curTime.tv_usec*1e-6;
00328 
00329     // if t < simTime, clock would go backwards. this would be bad...
00330     // (this could happen as timeval has a lower number of digits that simtime_t)
00331     if (t < simTime()) t = simTime();
00332 
00333     msg->setSentFrom(mod, -1, simTime());
00334     msg->setArrival(mod,-1,t);
00335     simulation.msgQueue.insert(msg);
00336 }

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

Definition at line 74 of file realtimescheduler.cc.

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

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

void RealtimeScheduler::startRun (  )  [virtual]

Called at the beginning of a simulation run.

Definition at line 46 of file realtimescheduler.cc.

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


Member Data Documentation

fd_set RealtimeScheduler::all_fds [protected]

Definition at line 83 of file realtimescheduler.h.

Referenced by receiveWithTimeout(), sendBytes(), and setInterfaceModule().

cModule* RealtimeScheduler::appModule [protected]
SOCKET RealtimeScheduler::apptun_fd [protected]
timeval RealtimeScheduler::baseTime [protected]

Definition at line 89 of file realtimescheduler.h.

Referenced by executionResumed(), getNextEvent(), sendNotificationMsg(), and startRun().

size_t RealtimeScheduler::buffersize [protected]

Definition at line 77 of file realtimescheduler.h.

Referenced by receiveWithTimeout(), sendBytes(), and setInterfaceModule().

SOCKET RealtimeScheduler::maxfd [protected]
cModule* RealtimeScheduler::module [protected]

Definition at line 74 of file realtimescheduler.h.

Referenced by getNextEvent(), receiveWithTimeout(), setInterfaceModule(), and startRun().

SOCKET RealtimeScheduler::netw_fd [protected]
cMessage* RealtimeScheduler::notificationMsg [protected]

Definition at line 75 of file realtimescheduler.h.

Referenced by receiveWithTimeout(), setInterfaceModule(), and startRun().

Definition at line 76 of file realtimescheduler.h.

Referenced by receiveWithTimeout(), and setInterfaceModule().


The documentation for this class was generated from the following files:
Generated on Wed May 26 16:21:19 2010 for OverSim by  doxygen 1.6.3