#include <realtimescheduler.h>
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 |
PacketBuffer * | packetBuffer |
size_t | buffersize |
cModule * | appModule |
cMessage * | appNotificationMsg |
PacketBuffer * | appPacketBuffer |
size_t | appBuffersize |
int | appConnectionLimit |
int | additional_fd |
timeval | baseTime |
Classes | |
class | PacketBufferEntry |
typedef std::list<PacketBufferEntry> RealtimeScheduler::PacketBuffer |
RealtimeScheduler::RealtimeScheduler | ( | ) |
Constructor.
00033 : cScheduler() 00034 { 00035 FD_ZERO(&all_fds); 00036 maxfd = -1; 00037 netw_fd = -1; 00038 additional_fd = -1; 00039 }
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().
bool RealtimeScheduler::receiveWithTimeout | ( | long | usec | ) | [protected, virtual] |
Waits for incoming data on the tun device.
usec | Timeout after which to quit waiting (in µsec) |
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.
targetTime | stop waiting after this time is up |
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::executionResumed | ( | ) | [virtual] |
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.
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
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.
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. |
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 }
fd_set RealtimeScheduler::all_fds [protected] |
int RealtimeScheduler::maxfd [protected] |
int RealtimeScheduler::netw_fd [protected] |
cModule* RealtimeScheduler::module [protected] |
Referenced by getNextEvent(), receiveWithTimeout(), setInterfaceModule(), and startRun().
cMessage* RealtimeScheduler::notificationMsg [protected] |
Referenced by receiveWithTimeout(), setInterfaceModule(), and startRun().
PacketBuffer* RealtimeScheduler::packetBuffer [protected] |
Referenced by receiveWithTimeout(), and setInterfaceModule().
size_t RealtimeScheduler::buffersize [protected] |
Referenced by receiveWithTimeout(), sendBytes(), and setInterfaceModule().
cModule* RealtimeScheduler::appModule [protected] |
cMessage* RealtimeScheduler::appNotificationMsg [protected] |
PacketBuffer* RealtimeScheduler::appPacketBuffer [protected] |
size_t RealtimeScheduler::appBuffersize [protected] |
Referenced by receiveWithTimeout(), sendBytes(), and setInterfaceModule().
int RealtimeScheduler::appConnectionLimit [protected] |
Referenced by UdpOutScheduler::additionalFD(), TunOutScheduler::additionalFD(), and startRun().
int RealtimeScheduler::additional_fd [protected] |
timeval RealtimeScheduler::baseTime [protected] |
Referenced by executionResumed(), getNextEvent(), sendNotificationMsg(), and startRun().