View Javadoc

1   package cz.cuni.amis.pogamut.udk.agent.navigation.martinnavigator;
2   
3   import java.util.Iterator;
4   import java.util.List;
5   import java.util.logging.Level;
6   import java.util.logging.Logger;
7   
8   import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
9   import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
10  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
11  import cz.cuni.amis.pogamut.udk.agent.module.sensor.AgentInfo;
12  import cz.cuni.amis.pogamut.udk.agent.navigation.AbstractUDKPathNavigator;
13  import cz.cuni.amis.pogamut.udk.agent.navigation.IUDKPathRunner;
14  import cz.cuni.amis.pogamut.udk.bot.command.AdvancedLocomotion;
15  import cz.cuni.amis.pogamut.udk.bot.impl.UDKBot;
16  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.NavPoint;
17  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.NavPointNeighbourLink;
18  import cz.cuni.amis.utils.NullCheck;
19  
20  /**
21   * Responsible for navigation to location.
22   *
23   * @author Jimmy
24   */
25  public class MartinNavigator<PATH_ELEMENT extends ILocated> extends AbstractUDKPathNavigator<PATH_ELEMENT>
26  {
27     
28  	/*========================================================================*/
29  
30      /**
31       * Distance, which is considered as close enough..
32       */
33      public static final int CLOSE_ENOUGH = 60;
34      public static final int PRECISION = 100; //empirically it was shown that 60 and 75 is sometimes not achieved. 
35  
36      @Override
37      public double getPrecision() {
38          return PRECISION; 
39      }
40      
41      /**
42       * Current navigation destination.
43       */
44      private Location navigDestination = null;
45  
46      /**
47       * Current stage of the navigation.
48       */
49      private Stage navigStage = Stage.COMPLETED;
50  
51      
52      /*========================================================================*/
53      
54  	@Override
55  	protected void navigate(int pathElementIndex) {
56  		switch (navigStage = keepNavigating()) {
57  		case NAVIGATING:
58  		case REACHING:	
59  			// ALL OK, we're running fine
60  			return;
61  		case CRASHED:
62  			// DAMN!
63  			executor.stuck();
64  			return;
65  		case COMPLETED:
66  			// HURRAY!
67  			executor.targetReached();
68  			return;
69  		}
70  	}
71  	
72  	@Override
73  	public void reset() {
74  		// reinitialize the navigator's values
75  		navigCurrentLocation = null;
76  		navigCurrentNode = null;
77          navigCurrentLink = null;
78  		navigDestination = null;
79  		navigIterator = null;
80  		navigNextLocation = null;
81  		navigNextNode = null;
82  		navigNextLocationOffset = 0;
83  		navigStage = Stage.COMPLETED;
84  	}
85  
86  	@Override
87  	public void newPath(List<PATH_ELEMENT> path) {
88  		// 1) obtain the destination
89  		Location dest = path.get(path.size()-1).getLocation();
90  				
91  		// 2) init the navigation
92  		initPathNavigation(dest, path);		
93  	}
94  	
95  	 /**
96       * Initializes navigation to the specified destination along specified path.
97       * @param destination Destination of the navigation.
98       * @param path Navigation path to the destination.
99       */
100     protected void initPathNavigation(Location destination, List<PATH_ELEMENT> path)
101     {
102         // init the navigation
103         if (log != null && log.isLoggable(Level.FINE)) 
104         	log.fine (
105         			"LoqueNavigator.initPathNavigation(): initializing path navigation"
106         			+ ", nodes " + path.size ()
107         	);
108         // init path navigation
109         if (!initAlongPath(destination, path))
110         {
111             // do it directly then..
112             initDirectNavigation(destination);
113         }
114     }
115     
116     /**
117      * Initializes navigation along path.
118      * @param dest Destination of the navigation.
119      * @param path Path of the navigation.
120      * @return True, if the navigation is successfuly initialized.
121      */
122     protected boolean initAlongPath(Location dest, List<PATH_ELEMENT> path)
123     {
124         // setup navigation info
125         navigDestination = dest;
126         navigIterator = path.iterator();
127         // reset current node
128         navigCurrentLocation = null;
129         navigCurrentNode = null;
130         // prepare next node
131         prepareNextNode();
132         // reset navigation stage
133         navigStage = Stage.NAVIGATING;
134         // reset node navigation info
135         return switchToNextNode();
136     }
137 	
138 	/**
139      * Initializes direct navigation to the specified destination.
140      * @param dest Destination of the navigation.
141      * @param timeout Maximum timeout of the navigation. Use 0 to auto-timeout.
142      */
143     protected void initDirectNavigation (Location dest)
144     {
145         initDirectly (dest);
146     }
147 	
148     /**
149      * Initializes direct navigation to given destination.
150      * 
151      * @param dest Destination of the navigation.
152      * @return Next stage of the navigation progress.
153      */
154     protected Stage initDirectly(Location dest)
155     {
156         // setup navigation info
157         navigDestination = dest;
158         // init runner
159         runner.reset();
160         // reset navigation stage
161         return navigStage = Stage.REACHING;
162     }
163     
164     /*========================================================================*/
165     
166     /**
167      * Navigates with the current navigation request.
168      * @return Stage of the navigation progress.
169      */
170     protected Stage keepNavigating ()
171     {
172         // is there any point in navigating further?
173         if (navigStage.terminated)
174             return navigStage;
175       
176         // try to navigate
177         switch (navigStage)
178         {
179             case REACHING:
180                 navigStage = navigDirectly();
181                 break;
182             default:
183                 navigStage = navigAlongPath();
184                 break;
185         }
186 
187         // return the stage
188         if (log != null && log.isLoggable(Level.FINEST)) log.finest ("Navigator.keepNavigating(): navigation stage " + navigStage);
189         return navigStage;
190     }
191 
192     /*========================================================================*/    
193 
194     /**
195      * Tries to navigate the agent directly to the navig destination.
196      * @return Next stage of the navigation progress.
197      */
198     private Stage navigDirectly ()
199     {
200         // get the distance from the target
201         int distance = (int) memory.getLocation().getDistance(navigDestination);
202 
203         // are we there yet?
204         if (distance <= getPrecision())
205         {
206             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigDirectly(): destination close enough: " + distance);
207             return Stage.COMPLETED;
208         }
209 
210         // run to that location..
211         if (!runner.runToLocation (navigDestination, null, navigDestination, null, true))
212         {
213             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigDirectly(): direct navigation failed");
214             return Stage.CRASHED;
215         }
216 
217         // well, we're still running
218         if (log != null && log.isLoggable(Level.FINEST)) log.finest ("LoqueNavigator.navigDirectly(): traveling directly, distance = " + distance);
219         return navigStage;
220     }
221 
222     /*========================================================================*/
223 
224     /**
225      * Iterator through navigation path.
226      */
227     private Iterator<PATH_ELEMENT> navigIterator = null;
228     
229     /**
230      * How many path elements we have iterated over before selecting the current {@link MartinNavigator#navigNextLocation}.
231      */
232     private int navigNextLocationOffset = 0;
233 
234     /**
235      * Current node in the path (the one the agent is running to).
236      */
237     private Location navigCurrentLocation = null;
238     
239     /**
240      * If {@link MartinNavigator#navigCurrentLocation} is a {@link NavPoint} or has NavPoint near by,
241      * its instance is written here (null otherwise).
242      */
243     private NavPoint navigCurrentNode = null;
244 
245     /**
246      * If moving between two NavPoints {@link NavPoint} the object {@link NeighbourLink} holding infomation
247      * about the link (if any) will be stored here (null otherwise).
248      */
249     private NavPointNeighbourLink navigCurrentLink = null;
250 
251     /**
252      * Next node in the path (the one being prepared).
253      */
254     private Location navigNextLocation = null;
255     
256     /**
257      * If {@link MartinNavigator#navigNextLocation} is a {@link NavPoint} or has NavPoint near by,
258      * its instance is written here (null otherwise).
259      */
260     private NavPoint navigNextNode = null;
261     
262     /**
263      * Returns {@link NavPoint} instance for a given location. If there is no navpoint in the vicinity of {@link MartinNavigator#CLOSE_ENOUGH}
264      * null is returned.
265      * 
266      * @param location
267      * @return
268      */
269     protected NavPoint getNavPoint(ILocated location) {
270     	if (location instanceof NavPoint) return (NavPoint) location;
271     	NavPoint np = DistanceUtils.getNearest(main.getWorldView().getAll(NavPoint.class).values(), location);
272     	if (np.getLocation().getDistance(location.getLocation()) < CLOSE_ENOUGH) return np;
273     	return null;
274     }
275 
276    
277 
278     /**
279      * Tries to navigate the agent safely along the navigation path.
280      * @return Next stage of the navigation progress.
281      */
282     private Stage navigAlongPath()
283     {
284         // get the distance from the destination
285         int totalDistance = (int) memory.getLocation().getDistance(navigDestination);
286 
287         // are we there yet?
288         if (totalDistance <= getPrecision())
289         {
290             log.finest ("Navigator.navigAlongPath(): destination close enough: " + totalDistance);
291             return Stage.COMPLETED;
292         }
293         
294         return navigToCurrentNode();        
295     }
296 
297     /*========================================================================*/
298 
299     /**
300      * Prepares next navigation node in path.
301      * <p><p>
302      * If necessary just recalls {@link MartinNavigator#prepareNextNodeTeleporter()}.
303      */
304     private void prepareNextNode ()
305     {
306     	// retreive the next node, if there are any left
307         // note: there might be null nodes along the path!
308     	ILocated located = null;
309         navigNextLocation = null;
310         navigNextLocationOffset = 0;
311         while ((located == null) && navigIterator.hasNext ())
312         {
313             // get next node in the path
314         	located = navigIterator.next();
315         	navigNextLocationOffset += 1;
316         	if (located == null) {
317         		continue;            
318         	}
319         }
320 
321         // did we get the next node?
322         if (located == null) {
323         	navigNextLocationOffset = 0;
324         	return;
325         }
326         
327         if (executor.getPathElementIndex() + navigNextLocationOffset >= executor.getPath().size()) {
328         	navigNextLocationOffset = 0; // WTF?
329         }
330        
331         // obtain next location
332         navigNextLocation = located.getLocation();
333         // obtain navpoint instance for a given location
334         navigNextNode = getNavPoint(located);
335     }
336     
337     /**
338      * Initializes next navigation node in path.
339      * @return True, if the navigation node is successfully switched.
340      */
341     private boolean switchToNextNode ()
342     {
343         // move the current node into last node
344         Location navigLastLocation = navigCurrentLocation;
345         NavPoint navigLastNode = navigCurrentNode;
346 
347         // get the next prepared node
348         if (null == (navigCurrentLocation = navigNextLocation))
349         {
350             // no nodes left there..
351             if (log != null && log.isLoggable(Level.FINER)) log.finer ("Navigator.switchToNextNode(): no nodes left");
352             navigCurrentNode = null;
353             return false;
354         }
355         // rewrite the navpoint as well
356         navigCurrentNode = navigNextNode;
357 
358         // store current NavPoint link
359         navigCurrentLink = getNavPointsLink(navigLastNode, navigCurrentNode);
360         
361         // ensure that the last node is not null
362         if (navigLastLocation == null) {
363             navigLastLocation = navigCurrentLocation;
364             navigLastNode = navigCurrentNode;
365         }
366 
367         // get next node distance
368         int localDistance = (int) memory.getLocation().getDistance(navigCurrentLocation.getLocation());        
369 
370         if (navigCurrentNode == null) {
371         	// we do not have extra information about the location we're going to reach
372         	runner.reset();
373         	if (log != null && log.isLoggable(Level.FINE)) 
374         		log.fine (
375                     "LoqueNavigator.switchToNextNode(): switch to next location " + navigCurrentLocation
376                     + ", distance " + localDistance
377                     
378                 );
379         } else {
380             // init the runner
381 	        runner.reset();
382 	        
383 	        // switch to next node
384 	        if (log != null && log.isLoggable(Level.FINE)) 
385 	        	log.fine (
386 		            "LoqueNavigator.switchToNextNode(): switch to next node " + navigCurrentNode.getId().getStringId()
387 		            + ", distance " + localDistance
388 		            + ", reachable " + isReachable(navigCurrentNode)
389 		        );
390         }
391 
392         // tell the executor that we have moved in the path to the next element
393         if (executor.getPathElementIndex() < 0) {
394         	executor.switchToAnotherPathElement(0);
395         } else {
396         	if (navigNextLocationOffset > 0) {
397         		executor.switchToAnotherPathElement(executor.getPathElementIndex()+navigNextLocationOffset);
398         	} else {
399         		executor.switchToAnotherPathElement(executor.getPathElementIndex());
400         	}        	
401         }
402         navigNextLocationOffset = 0;
403         
404         return true;
405     }
406 
407     private boolean isReachable(NavPoint node) {
408     	if (node == null) return true;
409 		int hDistance = (int) memory.getLocation().getDistance2D(node.getLocation());
410 		int vDistance = (int) node.getLocation().getDistanceZ(memory.getLocation());
411 		double angle; 
412 		if (hDistance == 0) {
413 			angle = vDistance == 0 ? 0 : (vDistance > 0 ? Math.PI/2 : -Math.PI/2);
414 		} else {
415 			angle = Math.atan(vDistance / hDistance);
416 		}
417 		return Math.abs(vDistance) < 30 && Math.abs(angle) < Math.PI / 4;
418 	}
419 
420 
421 
422 	/*========================================================================*/
423 
424     /**
425      * Gets the link with movement information between two navigation points. Holds
426      * information about how we can traverse from the start to the end navigation
427      * point.
428      * 
429      * @return NavPointNeighbourLink or null
430      */
431     private NavPointNeighbourLink getNavPointsLink(NavPoint start, NavPoint end) {
432         if (start == null) {
433             //if start NavPoint is not provided, we try to find some
434             NavPoint tmp = getNavPoint(memory.getLocation());
435             if (tmp != null)
436                 start = tmp;
437             else
438                 return null;
439         }
440         if (end == null)
441             return null;
442 
443         if (end.getIncomingEdges().containsKey(start.getId()))
444             return end.getIncomingEdges().get(start.getId());
445         
446         return null;
447     }
448 
449     /*========================================================================*/
450 
451     /**
452      * Tries to navigate the agent safely to the current navigation node.
453      * @return Next stage of the navigation progress.
454      */
455     private Stage navigToCurrentNode ()
456     {
457     	if (navigCurrentNode != null) {
458     		// update location of the current place we're reaching
459     		navigCurrentLocation = navigCurrentNode.getLocation();
460     	}
461     	
462         // get the distance from the current node
463         int localDistance = (int) memory.getLocation().getDistance(navigCurrentLocation.getLocation());
464         // get the distance from the current node (neglecting jumps)
465         int localDistance2 = (int) memory.getLocation().getDistance(
466             Location.add(navigCurrentLocation.getLocation(), new Location (0,0,100))
467         );
468 
469         // where are we going to run to
470         Location firstLocation = navigCurrentLocation.getLocation();
471         // where we're going to continue
472         Location secondLocation = (navigNextNode != null && !navigNextNode.isLiftCenter() && !navigNextNode.isLiftCenter() ? 
473         		                  	navigNextNode.getLocation() :
474         		                  	navigNextLocation);
475         // and what are we going to look at
476         Location focus = (navigNextLocation == null) ? firstLocation : navigNextLocation.getLocation();
477 
478         // run to the current node..
479         if (!runner.runToLocation (firstLocation, secondLocation, focus, navigCurrentLink, (navigCurrentNode == null ? true : isReachable(navigCurrentNode)))) {
480             if (log != null && log.isLoggable(Level.FINE)) log.fine ("LoqueNavigator.navigToCurrentNode(): navigation to current node failed");
481             return Stage.CRASHED;
482         }
483 
484         // we're still running
485         if (log != null && log.isLoggable(Level.FINEST)) log.finest ("LoqueNavigator.navigToCurrentNode(): traveling to current node, distance = " + localDistance);
486 
487         // are we close enough to think about the next node?
488 //        if ( (localDistance < 600) || (localDistance2 < 600) )
489 //        {
490             // prepare the next node only when it is not already prepared..
491             if ((navigCurrentNode != null && navigCurrentNode == navigNextNode) || navigCurrentLocation.equals(navigNextLocation))
492             {
493                 // prepare next node in the path
494                 prepareNextNode();
495             }
496 //        }
497 
498         int testDistance = 200; // default constant suitable for default running 
499         if (navigCurrentNode != null && (navigCurrentNode.isLiftCenter() || navigCurrentNode.isLiftExit())) {
500         	// if we should get to lift exit or the lift center, we must use more accurate constants
501         	testDistance = 150;
502         }
503             
504         // are we close enough to switch to the next node?
505         if ( (localDistance < testDistance) || (localDistance2 < testDistance) )
506         {
507             // switch navigation to the next node
508             if (!switchToNextNode ())
509             {
510                 // switch to the direct navigation
511                 if (log != null && log.isLoggable(Level.FINE)) log.fine ("Navigator.navigToCurrentNode(): switch to direct navigation");
512                 return initDirectly(navigDestination);
513             }
514         } else {
515         	// CHECK 2nd LOCATION THEN!
516         	if (navigNextLocation != null) {
517         		localDistance = (int) memory.getLocation().getDistance(navigNextLocation.getLocation());
518                 // get the distance from the current node (neglecting jumps)
519                 localDistance2 = (int) memory.getLocation().getDistance(
520                     Location.add(navigNextLocation.getLocation(), new Location (0,0,100))
521                 );
522                 
523                 if ( (localDistance < testDistance) || (localDistance2 < testDistance) )
524                 {
525                     // switch navigation to the next node
526                     if (!switchToNextNode ())
527                     {
528                         // switch to the direct navigation
529                         if (log != null && log.isLoggable(Level.FINE)) log.fine ("Navigator.navigToCurrentNode(): switch to direct navigation");
530                         return initDirectly(navigDestination);
531                     }
532                 }
533         	}
534         }
535 
536         // well, we're still running
537         return navigStage;
538     }
539 
540     /*========================================================================*/
541 
542     /**
543      * Enum of types of terminating navigation stages.
544      */
545     private enum TerminatingStageType {
546         /** Terminating with success. */
547         SUCCESS (false),
548         /** Terminating with failure. */
549         FAILURE (true);
550 
551         /** Whether the terminating with failure. */
552         public boolean failure;
553 
554         /**
555          * Constructor.
556          * @param failure Whether the terminating with failure.
557          */
558         private TerminatingStageType (boolean failure)
559         {
560             this.failure = failure;
561         }
562     };
563 
564     /**
565      * All stages the navigation can come to.
566      */
567     public enum Stage
568     {
569         /**
570          * Running directly to the destination.
571          */
572         REACHING ()
573         {
574             protected Stage next () { return this; }
575         },
576         /**
577          * Navigating along the path.
578          */
579         NAVIGATING ()
580         {
581             protected Stage next () { return this; }
582         },
583         /**
584          * Navigation failed because of troublesome obstacles.
585          */
586         CRASHED (TerminatingStageType.FAILURE)
587         {
588             protected Stage next () { return this; }
589         },
590         /**
591          * Navigation finished successfully.
592          */
593         COMPLETED (TerminatingStageType.SUCCESS)
594         {
595             protected Stage next () { return this; }
596         };
597         
598         /*====================================================================*/
599 
600         /**
601          * Whether the nagivation is terminated.
602          */
603         public boolean terminated;
604         /**
605          * Whether the navigation has failed.
606          */
607         public boolean failure;
608        
609         /*====================================================================*/
610 
611         /**
612          * Constructor: Not finished, not failed
613          */
614         private Stage ()
615         {
616             this.terminated = false;
617             this.failure = false;
618         }
619 
620         /**
621          * Constructor: terminating.
622          * @param type Type of terminating navigation stage.
623          */
624         private Stage (TerminatingStageType type)
625         {
626             this.terminated = true;
627             this.failure = type.failure;
628         }
629 
630         /*====================================================================*/
631 
632         /**
633          * Retreives the next step of navigation sequence the stage belongs to.
634          * @return The next step of navigation sequence. Note: Some stages are
635          * not part of any logical navigation sequence. In such cases, this
636          * method simply returns the same stage.
637          */
638         protected abstract Stage next ();
639 
640         /*====================================================================*/
641         
642     }
643 
644     /*========================================================================*/
645 
646     /**
647      * Default: Loque Runner.
648      */
649     private IUDKPathRunner runner;
650 
651     /*========================================================================*/
652 
653     /** Agent's main. */
654     protected UDKBot main;
655     /** Loque memory. */
656     protected AgentInfo memory;
657     /** Agent's body. */
658     protected AdvancedLocomotion body;
659     /** Agent's log. */
660     protected Logger log;
661 
662     /*========================================================================*/
663 
664     /**
665      * Constructor.
666      * @param main Agent's main.
667      * @param memory Loque memory.
668      */
669     public MartinNavigator (UDKBot bot, Logger log)
670     {
671         // setup reference to agent
672         this.main = bot;
673         this.memory = new AgentInfo(bot);
674         this.body = new AdvancedLocomotion(bot, log);
675         this.log = log;
676 
677         // create runner object
678         this.runner = new MartinRunner(bot, memory, body, log);
679     }
680 
681     /**
682      * Constructor.
683      * @param main Agent's main.
684      * @param memory Loque memory.
685      */
686     public MartinNavigator (UDKBot bot, IUDKPathRunner runner, Logger log)
687     {
688         // setup reference to agent
689         this.main = bot;
690         this.memory = new AgentInfo(bot);
691         this.body = new AdvancedLocomotion(bot, log);
692         this.log = log;
693 
694         // save runner object
695         this.runner = runner;
696         NullCheck.check(this.runner, "runner");
697     }    
698     
699 }