OverSim
Quon.cc
Go to the documentation of this file.
1 //
2 // Copyright (C) 2006 Institut fuer Telematik, Universitaet Karlsruhe (TH)
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18 
25 #include <Quon.h>
26 #include <BootstrapList.h>
27 #include <limits>
28 
30 
31 void Quon::initializeOverlay(int stage)
32 {
33  // because of IPAddressResolver, we need to wait until interfaces are registered,
34  // address auto-assignment takes place etc.
35  if(stage != MIN_STAGE_OVERLAY) {
36  return;
37  }
38 
39  // fetch parameters
40  minAOI = (double)par("minAOIWidth") + (double)par("AOIBuffer"); // FIXME: use buffer only where required
41  maxAOI = (double)par("AOIWidth") + (double)par("AOIBuffer"); // FIXME: use buffer only where required
42  AOIWidth = maxAOI;
43  connectionLimit = par("connectionLimit");
44  areaDimension = par("areaDimension");
45  joinTimeout = par("joinTimeout");
46  deleteTimeout = par("deleteTimeout");
47  aliveTimeout = (double)par("aliveTimeout") / 2.0;
48  backupIntervall = par("contactBackupIntervall");
49  numBackups = par("numBackups");
50  linearAdaption = par("AOIAdaptLinear");
51  adaptionSensitivity = par("AOIAdaptionSensitivity");
52  gossipSensitivity = par("AOIGossipSensitivity");
53  useSquareMetric = par("useSquareMetric");
54  nnOnlyBinding = par("newNeighborsOnlyBinding");
55 
57 
58  loginCache = dynamic_cast<LoginCache*>(simulation.getModuleByPath("globalObserver.globalFunctions[0].function.loginCache"));
59  cacheInterval = par("cacheInterval");
60  timeSinceCache = 0;
61 
62  // determine wether we want dynamic AOI or not
64 
65  // set node key and thisSite pointer
67  thisSite = new QuonSite();
69  thisSite->type = QTHIS;
70 
71  // initialize self-messages
72  join_timer = new cMessage("join_timer");
73  sec_timer = new cMessage("sec_timer");
74  alive_timer = new cMessage("alive_timer");
75  backup_timer = new cMessage("backup_timer");
76 
77  // statistics
80  nodeMoveBytesSend = 0.0;
82  nodeLeaveBytesSend = 0.0;
85  bytesPerSecond = 0.0;
86  softConnections = 0;
90  secTimerCount = 0;
91  //rejoinCount = 0;
92  avgAOI= 0 ;
93  joinTime = 0;
94 
95  // watch some variables
96  WATCH(thisSite->address);
97  WATCH(thisSite->position);
98  WATCH(AOIWidth);
99  //WATCH_POINTER_MAP(Sites);
100  //WATCH_POINTER_MAP(deletedSites);
101  //WATCH_SET(Positions);
102  WATCH(joinRequestBytesSend);
104  WATCH(nodeMoveBytesSend);
105  WATCH(newNeighborsBytesSend);
106  WATCH(nodeLeaveBytesSend);
107  WATCH(maxBytesPerSecondSend);
108  WATCH(bytesPerSecond);
109  WATCH(softConnections);
110  WATCH(softNeighborCount);
111  WATCH(bindingNeighborCount);
112  WATCH(directNeighborCount);
113  //WATCH(rejoinCount);
114 
115  // set initial state
116  aloneInOverlay = false;
119 }
120 
122 {
123  this->qstate = qstate;
124  switch(qstate) {
125  case QUNINITIALIZED:
127  cancelEvent(join_timer);
128  cancelEvent(sec_timer);
129  cancelEvent(alive_timer);
130  cancelEvent(backup_timer);
131  break;
132  case QJOINING:
133  if( joinTime == 0 ) joinTime = simTime();
134  scheduleAt(simTime(), join_timer);
135  scheduleAt(simTime() + 1.0, sec_timer);
136  break;
137  case QREADY:
138  cancelEvent(join_timer);
139  RECORD_STATS(
141  "QuON: JoinTime",
142  SIMTIME_DBL(simTime()) - SIMTIME_DBL(joinTime) + par("addJoinDelay").doubleValue()
143  );
144  );
145  joinTime = 0;
148  // tell the application we are ready unless we are rejoining the overlay
149  //if(rejoinCount == 0) {
150  CompReadyMessage* readyMsg = new CompReadyMessage("OVERLAY_READY");
151  readyMsg->setReady(true);
152  readyMsg->setComp(getThisCompType());
153  // TODO/FIXME: use overlay->sendMessageToAllComp(msg, getThisCompType())?
154  sendToApp(readyMsg);
155  //}
156  // set initial AOI size
157  AOIWidth = maxAOI;
158  GameAPIResizeAOIMessage* gameMsg = new GameAPIResizeAOIMessage("RESIZE_AOI");
159  gameMsg->setCommand(RESIZE_AOI);
160  gameMsg->setAOIsize(AOIWidth);
161  sendToApp(gameMsg);
162  if(aliveTimeout > 0.0) {
163  scheduleAt(simTime() + aliveTimeout, alive_timer);
164  }
165  if(backupIntervall > 0.0) {
166  scheduleAt(simTime() + backupIntervall, backup_timer);
167  }
168  break;
169  }
171  // debug output
172  if(debugOutput) {
173  EV << "[Quon::changeState() @ " << thisNode.getIp()
174  << " (" << thisNode.getKey().toString(16) << ")]\n"
175  << " Node " << thisSite->address.getIp() << " entered ";
176  switch(qstate) {
177  case QUNINITIALIZED:
178  EV << "UNINITIALIZED";
179  break;
180  case QJOINING:
181  EV << "JOINING";
182  break;
183  case QREADY:
184  EV << "READY";
185  break;
186  }
187  EV << " state." << endl;
188  }
189 }
190 
191 void Quon::handleTimerEvent(cMessage* msg)
192 {
193  if(msg->isName("join_timer")) {
194  //reset timer
195  cancelEvent(join_timer);
196  if(qstate != QREADY) {
197  scheduleAt(simTime() + joinTimeout, msg);
198  // handle event
200  }
201  }
202  else if(msg->isName("sec_timer")) {
203  //reset timer
204  cancelEvent(sec_timer);
205  scheduleAt(simTime() + 1, msg);
206  // handle event
207  processSecTimer();
208  }
209  else if(msg->isName("delete_timer")) {
210  // handle event
211  processDeleteTimer(msg);
212  }
213  else if(msg->isName("alive_timer")) {
214  //reset timer
215  cancelEvent(alive_timer);
216  scheduleAt(simTime() + aliveTimeout, msg);
217  // handle event
219  }
220  else if(msg->isName("backup_timer")) {
221  //reset timer
222  cancelEvent(backup_timer);
223  scheduleAt(simTime() + backupIntervall, msg);
224  // handle event
226  }
227 }
228 
229 void Quon::handleAppMessage(cMessage* msg)
230 {
231  GameAPIMessage* gameAPIMsg = dynamic_cast<GameAPIMessage*>(msg);
232  if(gameAPIMsg != NULL) {
233  // debug output
234  if(debugOutput) {
235  EV << "[Quon::handleAppMessage() @ " << thisNode.getIp()
236  << " (" << thisNode.getKey().toString(16) << ")]\n"
237  << " Node " << thisSite->address.getIp() << " received " << gameAPIMsg->getName() << " from application."
238  << endl;
239  }
240  switch(gameAPIMsg->getCommand()) {
241  case MOVEMENT_INDICATION: {
242  GameAPIPositionMessage* gameAPIPositionMsg = dynamic_cast<GameAPIPositionMessage*>(msg);
243  if(qstate == QJOINING) {
244  handleJoin(gameAPIPositionMsg);
245  }
246  else if(qstate == QREADY) {
247  handleMove(gameAPIPositionMsg);
248  }
249  } break;
250  case GAMEEVENT_CHAT:
251  case GAMEEVENT_SNOW:
252  case GAMEEVENT_FROZEN: {
253  handleEvent(gameAPIMsg);
254  } break;
255  }
256  }
257  delete msg;
258 }
259 
261 {
262  if(qstate == QUNINITIALIZED) {
263  delete msg;
264  return;
265  }
266  QuonMessage* quonMsg = dynamic_cast<QuonMessage*>(msg);
267  if(quonMsg != NULL) {
268  // debug output
269  if(debugOutput) {
270  EV << "[Quon::handleUDPMessage() @ " << thisNode.getIp()
271  << " (" << thisNode.getKey().toString(16) << ")]\n"
272  << " Node " << thisSite->address.getIp() << " received " << quonMsg->getName() << " from " << quonMsg->getSender().getIp() << "."
273  << endl;
274  }
275  if(qstate == QREADY) {
276  switch(quonMsg->getCommand()) {
277  case JOIN_REQUEST: {
278  handleJoinRequest(quonMsg);
279  } break;
280  case NODE_MOVE: {
281  QuonMoveMessage* quonMoveMsg = dynamic_cast<QuonMoveMessage*>(msg);
282  handleNodeMove(quonMoveMsg);
283  delete msg;
284  } break;
285  case NEW_NEIGHBORS: {
286  QuonListMessage* quonListMsg = dynamic_cast<QuonListMessage*>(msg);
287  handleNewNeighbors(quonListMsg);
288  delete msg;
289  } break;
290  case NODE_LEAVE: {
291  QuonListMessage* quonListMsg = dynamic_cast<QuonListMessage*>(msg);
292  handleNodeLeave(quonListMsg);
293  delete msg;
294  } break;
295  case QUON_EVENT: {
296  sendToApp(quonMsg->decapsulate());
297  delete quonMsg;
298  } break;
299  }
300  }
301  else if(qstate == QJOINING && quonMsg->getCommand() == JOIN_ACKNOWLEDGE) {
302  QuonListMessage* quonListMsg = dynamic_cast<QuonListMessage*>(msg);
303  handleJoinAcknowledge(quonListMsg);
304  delete msg;
305  }
306  else {
307  delete msg;
308  }
309  }
310  else {
311  delete msg;
312  }
313 }
314 
315 bool Quon::addSite(Vector2D p, NodeHandle node, double AOI, bool isSoft, QUpdateType update)
316 {
317  aloneInOverlay = false;
318  QuonSiteMap::iterator itSites = Sites.find(node.getKey());
319  QDeleteMap::iterator delIt = deletedSites.find(node.getKey());
320  // add node if he is not in the delete list OR has changed position since
321  // put in the delete list. don't add node if he has signled his leave himself
322  // (i.e. his position in the delete list is 0,0)
323  if(node.getKey() != thisSite->address.getKey() &&
324  (delIt == deletedSites.end() || (delIt->second != Vector2D(0,0) && delIt->second != p) )){
325  if(itSites == Sites.end()) {
326  if(debugOutput) {
327  EV << "[Quon::addSite() @ " << thisNode.getIp()
328  << " (" << thisNode.getKey().toString(16) << ")]\n"
329  << " Site " << node.getIp() << " at " << p << " has been added to the list."
330  << endl;
331  }
332  QuonSite* temp = new QuonSite();
333  temp->position = p;
334  temp->address = node;
335  if(update == QDIRECT) {
336  temp->dirty = true;
337  }
338  temp->alive = true;
339  temp->type = QUNDEFINED;
340  temp->softNeighbor = isSoft;
341  temp->AOIwidth = AOI;
342 
343  Sites.insert(std::make_pair(temp->address.getKey(), temp));
344  }
345  else if(update == QDIRECT || !itSites->second->alive) {
346  if(debugOutput) {
347  EV << "[Quon::addSite() @ " << thisNode.getIp()
348  << " (" << thisNode.getKey().toString(16) << ")]\n"
349  << " Site " << node.getIp() << " at " << p << " has been updated in the list."
350  << endl;
351  }
352  itSites->second->position = p;
353  itSites->second->dirty = true;
354  itSites->second->alive = true;
355  itSites->second->softNeighbor = isSoft;
356  itSites->second->type = QUNDEFINED;
357  itSites->second->AOIwidth = AOI;
358  }
359  return true;
360  }
361  return false;
362 }
363 
365 {
366  if(debugOutput) {
367  EV << "[Quon::updateThisSite() @ " << thisNode.getIp()
368  << " (" << thisNode.getKey().toString(16) << ")]\n"
369  << " This Site position has been updated to " << p << "."
370  << endl;
371  }
372  thisSite->position = p;
373 }
374 
376 {
377  if(Sites.size() > 0) {
379  QuonSite* (*bindingCandidates)[4] = new QuonSite*[numBackups+1][4];
380  for( int i = 0; i <= numBackups; ++i ){
381  for( int ii = 0; ii < 4; ++ii ){
382  bindingCandidates[i][ii] = 0;
383  }
384  }
385  double (*bindingDistance)[4] = new double[numBackups+1][4];
386  for( int i = 0; i <= numBackups; ++i ){
387  for( int ii = 0; ii < 4; ++ii ){
388  bindingDistance[i][ii] = std::numeric_limits<double>::infinity();
389  }
390  }
391 
392  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
393  QuonAOI NeighborAOI(itSites->second->position, itSites->second->AOIwidth, useSquareMetric);
394  if(AOI.collide(itSites->second->position) || NeighborAOI.collide(thisSite->position)) {
395  if(itSites->second->type != QNEIGHBOR) {
396  itSites->second->type = QNEIGHBOR;
397  itSites->second->dirty = true;
398  }
399  }
400  else if(itSites->second->type != QUNDEFINED) {
401  itSites->second->type = QUNDEFINED;
402  itSites->second->dirty = true;
403  }
404  int quad = thisSite->position.getQuadrant( itSites->second->position );
405  double dist;
406  if( useSquareMetric ) {
407  dist = thisSite->position.xyMaxDistance(itSites->second->position);
408  } else {
409  dist = thisSite->position.distanceSqr(itSites->second->position);
410  }
411 
412  // if dist is smaller than the most far binding candidate
413  if( dist < bindingDistance[numBackups][quad] ){
414  // Go through list of binding candidates until distance to current candidate
415  // is greater than distance to new candidate (i.e. look where in the binding
416  // candidate list the node belongs)
417  int backupPos = numBackups-1;
418  while( backupPos >= 0 && dist < bindingDistance[backupPos][quad] ){
419  // move old candidate one position back in the queue to make
420  // room for new candidate
421  bindingCandidates[backupPos+1][quad] = bindingCandidates[backupPos][quad];
422  bindingDistance[backupPos+1][quad] = bindingDistance[backupPos][quad];
423  --backupPos;
424  }
425  // place new candidate at appropriate position in candidate list
426  bindingCandidates[backupPos+1][quad] = itSites->second;
427  bindingDistance[backupPos+1][quad] = dist;
428  }
429 
430  }
431  for( int i = 0; i < 4; ++i ){
432  if( bindingCandidates[0][i] ){
433  bindingCandidates[0][i]->type = QBINDING;
434  bindingCandidates[0][i]->dirty = true;
435  for( int ii = 1; ii <= numBackups; ++ii ){
436  if( bindingCandidates[ii][i] ){
437  bindingBackup[ii-1][i] = bindingCandidates[ii][i]->address;
438  }
439  }
440  }
441  }
442 
443  delete[] bindingCandidates;
444  delete[] bindingDistance;
445  }
446  else {
447  if( !aloneInOverlay) {
448  ++rejoinCount;
449 
452  }
453  }
454 }
455 
457 {
458  QuonSiteMap::iterator itSites = Sites.find(node.getKey());
459  if(itSites != Sites.end()) {
460  if(debugOutput) {
461  EV << "[Quon::deleteSite() @ " << thisNode.getIp()
462  << " (" << thisNode.getKey().toString(16) << ")]\n"
463  << " Site " << node.getIp() << " at " << itSites->second->position << " has been removed from the list."
464  << endl;
465  }
466  delete itSites->second;
467  Sites.erase(itSites);
468  return true;
469  }
470  return false;
471 }
472 
473 int Quon::purgeSites(QPurgeType purgeSoftSites)
474 {
475  int purged = 0;
476  QuonSiteMap::iterator itSites = Sites.begin();
477  while(itSites != Sites.end()) {
478  // Purge softNeighbors only if QPURGESOFT is set
479  if(itSites->second->type == QUNDEFINED && ( purgeSoftSites == QPURGESOFT || !itSites->second->softNeighbor) ) {
480  if(debugOutput) {
481  EV << "[Quon::purgeSites() @ " << thisNode.getIp()
482  << " (" << thisNode.getKey().toString(16) << ")]\n"
483  << " Site " << itSites->second->address.getIp() << " at " << itSites->second->position << " has been removed from the list.\n"
484  << " Status: " << ((itSites->second->type == QUNDEFINED) ? "QUNDEFINED" : "QSOFT")
485  << endl;
486  }
487  delete itSites->second;
488  Sites.erase(itSites++);
489  ++purged;
490  }
491  else {
492  ++itSites;
493  }
494  }
495  return purged;
496 }
497 
499 {
500  // adjust AOIWidth
501  double oldAOI = AOIWidth;
502  if( linearAdaption ) {
503  AOIWidth -= (maxAOI - minAOI) * ((double) Sites.size() - (double) connectionLimit) * adaptionSensitivity / (double) connectionLimit;
504  } else if( Sites.size() > 0 ){
505  AOIWidth *= (1-adaptionSensitivity) + (double) connectionLimit * adaptionSensitivity / (double) Sites.size();
506  }
507  if( gossipSensitivity > 0 && Sites.size() > 0 ) {
508  double avgNeighborAOI = 0;
509  for( QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites ){
510  avgNeighborAOI += itSites->second->AOIwidth;
511  }
512  avgNeighborAOI /= Sites.size();
513  AOIWidth = AOIWidth*(1-gossipSensitivity) + avgNeighborAOI*gossipSensitivity;
514  }
515  if(AOIWidth > maxAOI) {
516  AOIWidth = maxAOI;
517  }
518  else if(AOIWidth < minAOI) {
519  AOIWidth = minAOI;
520  }
521 
522  if( oldAOI != AOIWidth ){
523  GameAPIResizeAOIMessage* gameMsg = new GameAPIResizeAOIMessage("RESIZE_AOI");
524  gameMsg->setCommand(RESIZE_AOI);
525  gameMsg->setAOIsize(AOIWidth);
526  sendToApp(gameMsg);
527  }
528 }
529 
531 {
532  if(qstate == QREADY) {
533  CompReadyMessage* readyMsg = new CompReadyMessage("OVERLAY_FINISHED");
534  readyMsg->setReady(false);
535  readyMsg->setComp(getThisCompType());
536  // TODO/FIXME: use overlay->sendMessageToAllComp(msg, getThisCompType())?
537  sendToApp(readyMsg);
538  if(Sites.size() > 0) {
539  // generate node leave messages
540  QuonListMessage* quonListMsg = new QuonListMessage("NODE_LEAVE");
541  quonListMsg->setCommand(NODE_LEAVE);
542  quonListMsg->setSender(thisSite->address);
543  quonListMsg->setPosition(thisSite->position);
544  quonListMsg->setAOIsize(AOIWidth);
545  // fill neighbors list
546  quonListMsg->setNeighborHandleArraySize(Sites.size());
547  quonListMsg->setNeighborPositionArraySize(Sites.size());
548  int i = 0;
549  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
550  quonListMsg->setNeighborHandle(i, itSites->second->address);
551  quonListMsg->setNeighborPosition(i, itSites->second->position);
552  ++i;
553  }
554  quonListMsg->setBitLength(QUONLIST_L(quonListMsg));
555 
556  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
557  QuonListMessage* quonCopyMsg = new QuonListMessage(*quonListMsg);
558  sendMessage(quonCopyMsg, itSites->second->address);
559  }
560  delete quonListMsg;
561  }
563  }
564 }
565 
567 {
568  GameAPIMessage* gameMsg = new GameAPIMessage("MOVEMENT_REQUEST");
569  gameMsg->setCommand(MOVEMENT_REQUEST);
570  sendToApp(gameMsg);
571 }
572 
574 {
575  RECORD_STATS(
578  }
579  avgAOI += AOIWidth;
581  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
582  switch(itSites->second->type) {
583  case QNEIGHBOR:
585  break;
586  case QBINDING:
588  break;
589  case QUNDEFINED:
590  if( itSites->second->softNeighbor ){
592  }
593  break;
594  case QTHIS:
595  break;
596  }
597  }
598  ++secTimerCount;
599  );
600  if( loginCache ) {
601  ++timeSinceCache;
602  if( qstate == QREADY && timeSinceCache >= cacheInterval ){
604  timeSinceCache = 0;
605  }
606  }
607  bytesPerSecond = 0.0;
608 }
609 
610 void Quon::processDeleteTimer(cMessage* msg)
611 {
612  QuonSelfMessage* quonMsg = dynamic_cast<QuonSelfMessage*>(msg);
613  QDeleteMap::iterator itSite = deletedSites.find(quonMsg->getKey());
614  if(itSite != deletedSites.end()) {
615  deletedSites.erase(itSite);
616  }
617  cancelAndDelete(quonMsg);
618 }
619 
621 {
622  bool rebuild = false;
623  QuonSiteMap::iterator itSites = Sites.begin();
624  while(itSites != Sites.end()) {
625  if(itSites->second->alive) {
626  itSites->second->alive = false;
627  ++itSites;
628  }
629  else {
630  NodeHandle node = itSites->second->address;
631  QuonSelfMessage* msg = new QuonSelfMessage("delete_timer");
632  msg->setKey(node.getKey());
633  scheduleAt(simTime() + deleteTimeout, msg);
634  deletedSites.insert(std::make_pair(node.getKey(), itSites->second->position));
635  ++itSites;
636  deleteSite(node);
637  // update simple client
638  deleteAppNeighbor(node);
639  if(!rebuild) {
640  rebuild = true;
641  }
642  }
643  }
644  if(rebuild) {
645  classifySites();
646  // update simple client
648  purgeSites();
649  }
650 }
651 
653 {
654  QuonMoveMessage* quonMoveMsg = new QuonMoveMessage("NODE_MOVE");
655  quonMoveMsg->setCommand(NODE_MOVE);
656  quonMoveMsg->setSender(thisSite->address);
657  quonMoveMsg->setPosition(thisSite->position);
658  quonMoveMsg->setAOIsize(AOIWidth);
659  quonMoveMsg->setNewPosition(thisSite->position);
660  quonMoveMsg->setIsBinding(true);
661  for(unsigned int i=0; i<4; i++) {
662  for( int ii = 0; ii < numBackups; ++ii ){
663  if(!bindingBackup[ii][i].isUnspecified()) {
664  QuonMoveMessage* copyMsg = new QuonMoveMessage(*quonMoveMsg);
665  copyMsg->setBitLength(QUONMOVE_L(copyMsg));
666  sendMessage(copyMsg, bindingBackup[ii][i]);
667  }
668  }
669  }
670  delete quonMoveMsg;
671 }
672 
674 {
675  TransportAddress joinNode;
676  if( loginCache ) {
677  joinNode = loginCache->getLoginNode( gameMsg->getPosition() );
678  } else {
679  joinNode = bootstrapList->getBootstrapNode();
680  }
681  thisSite->position = gameMsg->getPosition();
682  // check if this is the only node in the overlay
683  if(joinNode.isUnspecified()) {
685  aloneInOverlay = true;
686  }
687  else {
688  QuonMessage* quonMsg = new QuonMessage("JOIN_REQUEST");
689  quonMsg->setCommand(JOIN_REQUEST);
690  quonMsg->setSender(thisSite->address);
691  quonMsg->setPosition(thisSite->position);
692  quonMsg->setAOIsize(AOIWidth);
693  quonMsg->setBitLength(QUON_L(quonMsg));
694  sendMessage(quonMsg, joinNode);
695  }
696 }
697 
699 {
700  // adapt aoi
701  if(useDynamicAOI) {
702  adaptAoI();
703  classifySites();
704  }
705 
706  Vector2D position = gameMsg->getPosition();
707  // send position update to neighbors
708  QuonMoveMessage* quonMoveMsg = new QuonMoveMessage("NODE_MOVE");
709  quonMoveMsg->setCommand(NODE_MOVE);
710  quonMoveMsg->setSender(thisSite->address);
711  quonMoveMsg->setPosition(thisSite->position);
712  quonMoveMsg->setAOIsize(AOIWidth);
713  quonMoveMsg->setNewPosition(position);
714  quonMoveMsg->setBitLength(QUONMOVE_L(quonMoveMsg));
715 
716  QuonMoveMessage* quonMoveBindingMsg = new QuonMoveMessage(*quonMoveMsg);
717  quonMoveBindingMsg->setNeighborHandleArraySize(Sites.size());
718  quonMoveBindingMsg->setNeighborPositionArraySize(Sites.size());
719  int i = 0;
720  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
721  if(itSites->second->type == QBINDING || itSites->second->softNeighbor ) {
722  quonMoveBindingMsg->setNeighborHandle(i, itSites->second->address);
723  quonMoveBindingMsg->setNeighborPosition(i, itSites->second->position);
724  ++i;
725  }
726  }
727  quonMoveBindingMsg->setNeighborHandleArraySize(i);
728  quonMoveBindingMsg->setNeighborPositionArraySize(i);
729  if(i > 0) {
730  // speedhack:
731  // instead of building individual MoveMessages for every binding and softstate neighbor,
732  // we just send all binding/soft to every other binding/soft neighbor and pretend we did not send neighbors their own neighborslistentry
733  quonMoveBindingMsg->setBitLength(QUONMOVE_L(quonMoveBindingMsg) - QUONENTRY_L);
734  }
735 
736  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
737  QuonMoveMessage* copyMsg;
738  if(itSites->second->type == QBINDING || itSites->second->softNeighbor ) {
739  copyMsg = new QuonMoveMessage(*quonMoveBindingMsg);
740  if(itSites->second->type == QBINDING) {
741  copyMsg->setIsBinding(true);
742  }
743  else {
744  ++softConnections;
745  }
746  }
747  else {
748  copyMsg = new QuonMoveMessage(*quonMoveMsg);
749  }
750  sendMessage(copyMsg, itSites->second->address);
751  }
752  delete quonMoveMsg;
753  delete quonMoveBindingMsg;
754 
755  // update position
756  updateThisSite(position);
757  classifySites();
758  // update simple client
761 }
762 
764 {
765  // send event to neighbors
766  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
767  QuonEventMessage *quonMsg = new QuonEventMessage("EVENT");
768  quonMsg->setCommand(QUON_EVENT);
769  quonMsg->encapsulate((cPacket*)msg->dup());
770  // FIXME: Message length!
771  sendMessage(quonMsg, itSites->second->address);
772  }
773 }
774 
776 {
777  Vector2D joinPosition = quonMsg->getPosition();
778  // start with this node
779  double min_dist = thisSite->position.distanceSqr(joinPosition);
780  QuonSite* forwardSite = thisSite;
781  // iterate through all neighbors
782  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
783  if(itSites->second->position.distanceSqr(joinPosition) < min_dist) { //FIXME: use xy metric if desired?
784  min_dist = itSites->second->position.distanceSqr(joinPosition);
785  forwardSite = itSites->second;
786  }
787  }
788 
789  // do nothing and let node retry with new position if current position is illegal
790  if(min_dist == 0.0) {
791  delete quonMsg;
792  }
793  else if(forwardSite->type == QTHIS) {
794  QuonListMessage* quonListMsg = new QuonListMessage("JOIN_ACKNOWLEDGE");
795  quonListMsg->setCommand(JOIN_ACKNOWLEDGE);
796  quonListMsg->setSender(thisSite->address);
797  quonListMsg->setPosition(thisSite->position);
798  quonListMsg->setAOIsize(AOIWidth);
799  // fill neighbors list
800  quonListMsg->setNeighborHandleArraySize(Sites.size());
801  quonListMsg->setNeighborPositionArraySize(Sites.size());
802  int i = 0;
803  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
804  quonListMsg->setNeighborHandle(i, itSites->second->address);
805  quonListMsg->setNeighborPosition(i, itSites->second->position);
806  ++i;
807  }
808  quonListMsg->setNeighborHandleArraySize(i);
809  quonListMsg->setNeighborPositionArraySize(i);
810 
811  quonListMsg->setBitLength(QUONLIST_L(quonListMsg));
812  sendMessage(quonListMsg, quonMsg->getSender());
813  delete quonMsg;
814  }
815  else {
816  sendMessage(quonMsg, forwardSite->address);
817  }
818 }
819 
821 {
822  // add acceptor node
824  addSite(quonListMsg->getPosition(), quonListMsg->getSender(), quonListMsg->getAOIsize(), false, QDIRECT);
825  // add new neighbors
826  for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
827  addSite(quonListMsg->getNeighborPosition(i), quonListMsg->getNeighborHandle(i), quonListMsg->getAOIsize());
828  }
829  classifySites();
830  // update simple client
832  purgeSites();
833  // contact new neighbors
834  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
835  QuonMoveMessage* quonMoveMsg = new QuonMoveMessage("NODE_MOVE");
836  quonMoveMsg->setCommand(NODE_MOVE);
837  quonMoveMsg->setSender(thisSite->address);
838  quonMoveMsg->setPosition(quonListMsg->getPosition());
839  quonMoveMsg->setAOIsize(AOIWidth);
840  quonMoveMsg->setNewPosition(thisSite->position);
841  if(itSites->second->type == QBINDING) {
842  quonMoveMsg->setIsBinding(true);
843  }
844  quonMoveMsg->setBitLength(QUONMOVE_L(quonMoveMsg));
845  sendMessage(quonMoveMsg, itSites->second->address);
846  }
847  bytesPerSecond = 0.0;
848 }
849 
851 {
852  RECORD_STATS(
854  "QuON: MoveDelay",
855  SIMTIME_DBL(simTime()) - SIMTIME_DBL(quonMoveMsg->getCreationTime())
856  );
857  );
858 
859  // IF node was marked for deletetion, remove it from the delete list
860  QDeleteMap::iterator delIt = deletedSites.find(quonMoveMsg->getSender().getKey());
861  if( delIt != deletedSites.end() ){
862  deletedSites.erase( delIt );
863  }
864 
865  // Compute old and new AOI of moving node
866  QuonAOI oldAOI(quonMoveMsg->getPosition(), quonMoveMsg->getAOIsize(), useSquareMetric);
867  QuonAOI newAOI(quonMoveMsg->getNewPosition(), quonMoveMsg->getAOIsize(), useSquareMetric);
868  if(useDynamicAOI) {
869  QuonSiteMap::iterator itSites = Sites.find(quonMoveMsg->getSender().getKey());
870  if(itSites != Sites.end() && itSites->second->AOIwidth < quonMoveMsg->getAOIsize()) {
871  oldAOI.resize(itSites->second->AOIwidth);
872  }
873  }
874 
875  addSite(quonMoveMsg->getNewPosition(), quonMoveMsg->getSender(), quonMoveMsg->getAOIsize(), quonMoveMsg->getIsBinding(), QDIRECT);
876  // add new neighbors
877  handleInvalidNode(quonMoveMsg);
878  for(unsigned int i=0; i<quonMoveMsg->getNeighborHandleArraySize(); i++) {
879  addSite(quonMoveMsg->getNeighborPosition(i), quonMoveMsg->getNeighborHandle(i), quonMoveMsg->getAOIsize());
880  }
881  classifySites();
882  // update simple client
884  purgeSites();
885 
886  // send new neighbors
887  if( nnOnlyBinding && !quonMoveMsg->getIsBinding() ) return; //{
888  //QuonSiteMap::iterator itSites = Sites.find(quonMoveMsg->getSender().getKey());
889  //if (itSites->second->type != QBINDING && ! itSites->second->softNeighbor ) return;
890  //}
891  QuonListMessage* quonListMsg = new QuonListMessage("NEW_NEIGHBORS");
892  quonListMsg->setCommand(NEW_NEIGHBORS);
893  quonListMsg->setSender(thisSite->address);
894  quonListMsg->setPosition(thisSite->position);
895  quonListMsg->setAOIsize(AOIWidth);
896 
897  quonListMsg->setNeighborHandleArraySize(Sites.size());
898  quonListMsg->setNeighborPositionArraySize(Sites.size());
899 
900  int i = 0;
901  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
902  if(quonMoveMsg->getSender() != itSites->second->address &&
903  !oldAOI.collide(itSites->second->position) &&
904  newAOI.collide(itSites->second->position)) {
905  quonListMsg->setNeighborHandle(i, itSites->second->address);
906  quonListMsg->setNeighborPosition(i, itSites->second->position);
907  ++i;
908  }
909  }
910 
911  if(i > 0) {
912  quonListMsg->setNeighborHandleArraySize(i);
913  quonListMsg->setNeighborPositionArraySize(i);
914  quonListMsg->setBitLength(QUONLIST_L(quonListMsg));
915  sendMessage(quonListMsg, quonMoveMsg->getSender());
916  }
917  else {
918  delete quonListMsg;
919  }
920 }
921 
923 {
924  addSite(quonListMsg->getPosition(), quonListMsg->getSender(), quonListMsg->getAOIsize(), false, QDIRECT);
925 
926  // add new neighbors
927  handleInvalidNode(quonListMsg);
928  for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
929  addSite(quonListMsg->getNeighborPosition(i), quonListMsg->getNeighborHandle(i), quonListMsg->getAOIsize());
930  }
931  classifySites();
932  // update simple client
934  purgeSites();
935 }
936 
938 {
939  deleteSite(quonListMsg->getSender());
940  // update simple client
941  deleteAppNeighbor(quonListMsg->getSender());
942 
943  // insert into delete list
944  QuonSelfMessage* msg = new QuonSelfMessage("delete_timer");
945  msg->setKey(quonListMsg->getSender().getKey());
946  scheduleAt(simTime() + deleteTimeout, msg);
947  deletedSites.insert(std::make_pair(quonListMsg->getSender().getKey(), Vector2D(0,0)));
948 
949  // add possible new neighbors
950  handleInvalidNode(quonListMsg);
951  for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
952  addSite(quonListMsg->getNeighborPosition(i), quonListMsg->getNeighborHandle(i), quonListMsg->getAOIsize(), true);
953  }
954  classifySites();
955  // update simple client
957  purgeSites();
958 }
959 
960 // XXX FIXME Disabled, may cause exessive traffic
962 {
963  return;
964  for(unsigned int i=0; i<quonListMsg->getNeighborHandleArraySize(); i++) {
965  if(deletedSites.find(quonListMsg->getNeighborHandle(i).getKey()) != deletedSites.end()) {
966  QuonListMessage* quonLeaveMsg = new QuonListMessage("NODE_LEAVE");
967  quonLeaveMsg->setCommand(NODE_LEAVE);
968  quonLeaveMsg->setSender(quonListMsg->getNeighborHandle(i));
969  quonLeaveMsg->setPosition(quonListMsg->getNeighborPosition(i));
970  quonLeaveMsg->setAOIsize(AOIWidth);
971  quonLeaveMsg->setNeighborHandleArraySize(Sites.size());
972  quonLeaveMsg->setNeighborPositionArraySize(Sites.size());
973  int i = 0;
974  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
975  if(itSites->second->type == QBINDING) {
976  quonLeaveMsg->setNeighborHandle(i, itSites->second->address);
977  quonLeaveMsg->setNeighborPosition(i, itSites->second->position);
978  ++i;
979  }
980  }
981  quonLeaveMsg->setNeighborHandleArraySize(i);
982  quonLeaveMsg->setNeighborPositionArraySize(i);
983  quonLeaveMsg->setBitLength(QUONLIST_L(quonLeaveMsg));
984  sendMessage(quonLeaveMsg, quonListMsg->getSender());
985  }
986  }
987 }
988 
990 {
991  GameAPIListMessage* gameMsg = new GameAPIListMessage("NEIGHBOR_UPDATE");
992  gameMsg->setCommand(NEIGHBOR_UPDATE);
993 
994  gameMsg->setRemoveNeighborArraySize(Sites.size());
995  gameMsg->setAddNeighborArraySize(Sites.size());
996  gameMsg->setNeighborPositionArraySize(Sites.size());
997 
998  int remSize, addSize;
999  remSize = addSize = 0;
1000  for(QuonSiteMap::iterator itSites = Sites.begin(); itSites != Sites.end(); ++itSites) {
1001  if(itSites->second->type == QUNDEFINED && (purgeSoftSites == QPURGESOFT || !itSites->second->softNeighbor) && itSites->second->dirty) {
1002  gameMsg->setRemoveNeighbor(remSize, itSites->second->address);
1003  ++remSize;
1004  }
1005  else if(itSites->second->dirty) {
1006  gameMsg->setAddNeighbor(addSize, itSites->second->address);
1007  gameMsg->setNeighborPosition(addSize, itSites->second->position);
1008  itSites->second->dirty = false;
1009  ++addSize;
1010  }
1011  }
1012 
1013  if(remSize > 0 || addSize > 0) {
1014  gameMsg->setRemoveNeighborArraySize(remSize);
1015  gameMsg->setAddNeighborArraySize(addSize);
1016  gameMsg->setNeighborPositionArraySize(addSize);
1017  sendToApp(gameMsg);
1018  }
1019  else {
1020  delete gameMsg;
1021  }
1022 }
1023 
1025 {
1026  GameAPIListMessage* gameMsg = new GameAPIListMessage("NEIGHBOR_UPDATE");
1027  gameMsg->setCommand(NEIGHBOR_UPDATE);
1028  gameMsg->setRemoveNeighborArraySize(1);
1029  gameMsg->setAddNeighborArraySize(0);
1030  gameMsg->setNeighborPositionArraySize(0);
1031  gameMsg->setRemoveNeighbor(0, node);
1032  sendToApp(gameMsg);
1033 }
1034 
1035 void Quon::sendToApp(cMessage* msg)
1036 {
1037  // debug output
1038  if(debugOutput) {
1039  EV << "[Quon::sendToApp() @ " << thisNode.getIp()
1040  << " (" << thisNode.getKey().toString(16) << ")]\n"
1041  << " Node " << thisSite->address.getIp() << " sending " << msg->getName() << " to application."
1042  << endl;
1043  }
1044  send(msg, "appOut");
1045 }
1046 
1047 void Quon::sendMessage(QuonMessage* quonMsg, NodeHandle destination)
1048 {
1049  // collect statistics
1050  RECORD_STATS(
1051  switch(quonMsg->getCommand()) {
1052  case JOIN_REQUEST:
1053  joinRequestBytesSend += quonMsg->getByteLength();
1054  break;
1055  case JOIN_ACKNOWLEDGE:
1056  joinAcknowledgeBytesSend += quonMsg->getByteLength();
1057  break;
1058  case NODE_MOVE:
1059  nodeMoveBytesSend += quonMsg->getByteLength();
1060  break;
1061  case NEW_NEIGHBORS:
1062  newNeighborsBytesSend += quonMsg->getByteLength();
1063  break;
1064  case NODE_LEAVE:
1065  nodeLeaveBytesSend += quonMsg->getByteLength();
1066  break;
1067  }
1068  if(qstate == QREADY) {
1069  bytesPerSecond += quonMsg->getByteLength();
1070  }
1071  );
1072 
1073  // debug output
1074  if(debugOutput) {
1075  EV << "[Quon::sendMessage() @ " << thisNode.getIp()
1076  << " (" << thisNode.getKey().toString(16) << ")]\n"
1077  << " Node " << thisSite->address.getIp() << " sending " << quonMsg->getName() << " to " << destination.getIp() << "."
1078  << endl;
1079  }
1080  sendMessageToUDP(destination, quonMsg);
1081 }
1082 
1084 {
1085  if(ev.isGUI()) {
1086  switch(qstate) {
1087  case QUNINITIALIZED:
1088  getParentModule()->getParentModule()->getDisplayString().setTagArg("i2", 1, "red");
1089  getDisplayString().setTagArg("i", 1, "red");
1090  break;
1091  case QJOINING:
1092  getParentModule()->getParentModule()->getDisplayString().setTagArg("i2", 1, "yellow");
1093  getDisplayString().setTagArg("i", 1, "yellow");
1094  break;
1095  case QREADY:
1096  getParentModule()->getParentModule()->getDisplayString().setTagArg("i2", 1, "green");
1097  getDisplayString().setTagArg("i", 1, "green");
1098  break;
1099  }
1100  }
1101 }
1102 
1104 {
1105  double overallBytesSend = joinRequestBytesSend
1110  if(overallBytesSend != 0.0) {
1111  // collect statistics in percent
1112  globalStatistics->addStdDev("Quon: fraction of JOIN_REQUEST bytes sent ", joinRequestBytesSend / overallBytesSend);
1113  globalStatistics->addStdDev("Quon: fraction of JOIN_ACKNOWLEDGE bytes sent", joinAcknowledgeBytesSend / overallBytesSend);
1114  globalStatistics->addStdDev("Quon: fraction of NODE_MOVE bytes sent", nodeMoveBytesSend / overallBytesSend);
1115  globalStatistics->addStdDev("Quon: fraction of NEW_NEIGHBORS bytes sent", newNeighborsBytesSend / overallBytesSend);
1116  globalStatistics->addStdDev("Quon: fraction of NODE_LEAVE bytes sent", nodeLeaveBytesSend / overallBytesSend);
1117  }
1118  globalStatistics->addStdDev("Quon: max bytes/second sent", maxBytesPerSecondSend);
1119 
1120 // We use our own time count to avoid rounding errors
1121 // simtime_t time = globalStatistics->calcMeasuredLifetime(creationTime);
1122 // if(time != 0.0) {
1123  if(secTimerCount != 0) {
1124  globalStatistics->addStdDev("Quon: average bytes/second sent", averageBytesPerSecondSend / (double) secTimerCount);
1125  globalStatistics->addStdDev("Quon: average direct-neighbor count", directNeighborCount / (double) secTimerCount);
1126  globalStatistics->addStdDev("Quon: average binding-neighbor count", bindingNeighborCount / (double) secTimerCount);
1127  globalStatistics->addStdDev("Quon: average soft-neighbor count", softNeighborCount / (double) secTimerCount);
1128  //globalStatistics->addStdDev("Quon: average rejoin count", rejoinCount);
1129  globalStatistics->addStdDev("Quon: average AOI width", avgAOI / (double) secTimerCount);
1130  }
1131 
1133 }
1134 
1136 {
1137  Enter_Method_Silent();
1138  return qstate;
1139 }
1140 
1142 {
1143  Enter_Method_Silent();
1144  return AOIWidth - (double)par("AOIBuffer");
1145 }
1146 
1148 {
1149  Enter_Method_Silent();
1150  return thisSite->position;
1151 }
1152 
1154 {
1155  Enter_Method_Silent();
1156  return areaDimension;
1157 }
1158 
1160 {
1161  Enter_Method_Silent();
1162  return thisSite->address.getKey();
1163 }
1164 
1166 {
1167  Enter_Method_Silent();
1168  long temp = softConnections;
1169  softConnections = 0;
1170  return temp;
1171 }
1172 
1174 {
1175  // destroy self timer messages
1176  cancelAndDelete(join_timer);
1177  cancelAndDelete(sec_timer);
1178  cancelAndDelete(alive_timer);
1179  cancelAndDelete(backup_timer);
1180  delete thisSite;
1181  QuonSiteMap::iterator itSites = Sites.begin();
1182  while(itSites != Sites.end()) {
1183  delete itSites->second;
1184  ++itSites;
1185  }
1186 }