View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.navigation;
2   
3   import java.util.EventListener;
4   import java.util.HashSet;
5   import java.util.Set;
6   import java.util.logging.Level;
7   
8   import cz.cuni.amis.pogamut.base.agent.navigation.IPathExecutorState;
9   import cz.cuni.amis.pogamut.base.agent.navigation.PathExecutorState;
10  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
11  import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
12  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
13  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.NavigationGraphBuilder;
14  import cz.cuni.amis.pogamut.ut2004.agent.navigation.astar.UT2004AStar;
15  import cz.cuni.amis.pogamut.ut2004.agent.navigation.floydwarshall.FloydWarshallMap;
16  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
17  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotDamaged;
18  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotKilled;
19  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
20  import cz.cuni.amis.pogamut.ut2004.communication.worldview.map.UT2004Map;
21  import cz.cuni.amis.utils.NullCheck;
22  import cz.cuni.amis.utils.flag.FlagListener;
23  import cz.cuni.amis.utils.listener.Listeners;
24  import cz.cuni.amis.utils.maps.CountIntMap;
25  
26  /**
27   * Use with care! This class automatically watching for PATH-STUCKs removing bad-edges directly from navigation graph.
28   * 
29   * Works ONLY IF YOU'RE using {@link FloydWarshallMap} ... does not alter map inside UT2004. 
30   * 
31   * The class will use {@link UT2004PathExecutor} to sense {@link PathExecutorState#STUCK}s, if the bot stucks and 
32   * {@link UT2004PathExecutor#getCurrentLink()} is available, this class will assume that this link is causing problems to
33   * bot's navigation and will start to count failures when navigating through this link.
34   * 
35   * When number of failures for some link exceeds configured number (removeBadEdgeAfterNFailures, default is: {@link UT2004PathAutoFixer#REAMOVE_EDGE_AFTER_N_FAILURES_DEFAULT}),
36   * it will {@link UT2004PathAutoFixer#removeLink(NavPointNeighbourLink)} remove it from bot's internal navigation graph.
37   * 
38   * Note that removal of some links requires recomputation of {@link FloydWarshallMap}, so you can pass some {@link FloydWarshallMap}
39   * instance into it to perform auto {@link FloydWarshallMap#refreshPathMatrix()} upon link removal.
40   * 
41   * @author Jimmy
42   */
43  public class UT2004PathAutoFixer {
44  	
45  	
46  	
47  	public static final int REAMOVE_EDGE_AFTER_N_FAILURES_DEFAULT = 2;
48  
49  	private IUT2004PathExecutor<? extends ILocated> pathExecutor;
50  	private FloydWarshallMap fwMap;
51  	private UT2004AStar aStar;
52  	private NavigationGraphBuilder navBuilder;
53  	
54  	private CountIntMap<NavPointNeighbourLink> badLinks = new CountIntMap<NavPointNeighbourLink>();
55  	
56  	private Set<NavPointNeighbourLink> removedLinks = new HashSet<NavPointNeighbourLink>();
57  	
58  	private LogCategory log;
59  
60  	private int removeBadEdgeAfterNFailures;
61  
62  	private boolean botHarmed;
63  
64  	private IWorldEventListener<BotDamaged> botDamagedListener = new IWorldEventListener<BotDamaged>() {
65  
66  		@Override
67  		public void notify(BotDamaged event) {
68  			botDamaged();			
69  		}
70  		
71  	};
72  	
73  	private IWorldEventListener<BotKilled> botKilledListener = new IWorldEventListener<BotKilled>() {
74  
75  		@Override
76  		public void notify(BotKilled event) {
77  			botKilled();			
78  		}
79  		
80  	};
81  
82  	/**
83  	 * UT2004PathAutoFixer will remove edge whenever bot fails {@link UT2004PathAutoFixer#REAMOVE_EDGE_AFTER_N_FAILURES_DEFAULT} times to walk through it.
84  	 * 
85  	 * @param bot
86  	 * @param pathExecutor
87  	 * @param fwMap can be null (won't auto-call {@link FloydWarshallMap#refreshPathMatrix()} then)
88  	 * @param aStar can be null (won't auto-call {@link UT2004AStar#mapChanged()} then)
89  	 * @param navBuilder
90  	 */
91  	public UT2004PathAutoFixer(UT2004Bot bot, IUT2004PathExecutor<? extends ILocated> pathExecutor, FloydWarshallMap fwMap, UT2004AStar aStar, NavigationGraphBuilder navBuilder) {
92  		this(bot, pathExecutor, fwMap, aStar, navBuilder, REAMOVE_EDGE_AFTER_N_FAILURES_DEFAULT);
93  	}
94  		
95  	/**
96  	 * UT2004PathAutoFixer will remove edge whenever bot fails 'removeBadEdgeAfterNFailures' times to walk through it.
97  	 * 
98  	 * @param bot
99  	 * @param pathExecutor
100 	 * @param fwMap can be null (won't auto-call {@link FloydWarshallMap#refreshPathMatrix()} then)
101 	 * @param aStar can be null (won't auto-call {@link UT2004AStar#mapChanged()} then)
102 	 * @param navBuilder
103 	 */
104 	public UT2004PathAutoFixer(UT2004Bot bot, IUT2004PathExecutor<? extends ILocated> pathExecutor, FloydWarshallMap fwMap, UT2004AStar aStar, NavigationGraphBuilder navBuilder, int removeBadEdgeAfterNFailures) {
105 		if (removeBadEdgeAfterNFailures < 1) {
106 			throw new IllegalArgumentException("removeBadEdgeAfterNFailures == " + removeBadEdgeAfterNFailures + " < 1 cannot be!");
107 		}
108 		this.log = bot.getLogger().getCategory(UT2004PathAutoFixer.class.getSimpleName());
109 		this.pathExecutor = pathExecutor;		
110 		this.fwMap = fwMap;
111 		this.aStar = aStar;
112 		this.navBuilder = navBuilder;		
113 		
114 		NullCheck.check(this.pathExecutor, "pathExecutor");
115 		NullCheck.check(this.navBuilder,   "navBuilder");
116 		
117 		this.removeBadEdgeAfterNFailures = removeBadEdgeAfterNFailures;
118 		
119 		this.pathExecutor.getState().addListener(new FlagListener<IPathExecutorState>() {
120 
121 			@Override
122 			public void flagChanged(IPathExecutorState changedValue) {
123 				switch (changedValue.getState()) {
124 				case PATH_COMPUTED:
125 					pathComputed();
126 					return;
127 				case SWITCHED_TO_ANOTHER_PATH_ELEMENT:
128 					switchedToNewElement();
129 					return;
130 				case STUCK:
131 					stuck();
132 					return;
133 					
134 				}
135 			}
136 			
137 		});
138 		
139 		bot.getWorldView().addEventListener(BotDamaged.class, botDamagedListener);
140 		bot.getWorldView().addEventListener(BotKilled.class, botKilledListener);
141 	}
142 
143 	protected void botDamaged() {
144 		// bot has been damaged, it might have moved it from the correct course
145 		botHarmed = true;
146 	}
147 
148 	protected void botKilled() {
149 		// bot has been damaged, it has moved it from the correct course
150 		botHarmed = true;
151 	}
152 
153 	protected void switchedToNewElement() {
154 		// we're heading to new element ...
155 		// => assuming we've correctly arrived to previous path point, so further navigation is OK
156 		botHarmed = false;
157 	}
158 	
159 	protected void pathComputed() {
160 		// we're on the new path!
161 		botHarmed = false;
162 	}
163 
164 	protected void stuck() {
165 		if (botHarmed) {
166 			// we have been hit by something, it might have caused navigation error
167 			// => ignore
168 			return;
169 		}
170 		
171 		NavPointNeighbourLink link = pathExecutor.getCurrentLink();
172 		if (link == null) return;
173 		
174 		badLinks.increase(link);	
175 		checkRemove(link);
176 	}
177 	
178 	/**
179 	 * Bot has stuck on 'link', {@link UT2004PathAutoFixer#badLinks} count has been increased, decide whether to remove the link from the graph.
180 	 * @param link
181 	 */
182 	protected void checkRemove(NavPointNeighbourLink link) {
183 		if (log != null && log.isLoggable(Level.WARNING)) log.warning("Bot has stuck (" + badLinks.get(link) + "x) on link " + link);
184 		if (badLinks.get(link) >= removeBadEdgeAfterNFailures) {
185 			listenerLink = link;
186 			listenersLinkCanRemove = true;
187 			linkRemovalListeners.notify(canRemoveLinkNotifier);
188 			if (listenersLinkCanRemove) {
189 				removeLink(link);
190 			} else {
191 				if (log != null && log.isLoggable(Level.WARNING)) log.warning("Some listener prevented link from removal: " + link);
192 			}
193 		}
194 	}
195 	
196 	/**
197 	 * Removes link from the graph + recompute fwMap path matrix.
198 	 * @param link
199 	 */
200 	protected void removeLink(NavPointNeighbourLink link) {
201 		String fromId = link.getFromNavPoint().getId().getStringId();
202 		String toId   = link.getToNavPoint().getId().getStringId();		
203 		if (log != null && log.isLoggable(Level.WARNING)) log.warning("REMOVING EDGE FROM NAV-GRAPTH (affects fwMap): " + fromId + " -> " + toId);
204 		navBuilder.removeEdge(fromId, toId);
205 		removedLinks.add(link);
206 		if (fwMap != null) fwMap.refreshPathMatrix();
207 		if (aStar != null) aStar.mapChanged();
208 		
209 		listenerLink = link;
210 		linkRemovalListeners.notify(linkRemovedNotifier);
211 	}
212 
213 	/**
214 	 * Returns set with all removed links.
215 	 * @return
216 	 */
217 	public Set<NavPointNeighbourLink> getRemovedLinks() {
218 		return removedLinks;
219 	}
220 
221 	/**
222 	 * Return counts of navigation failures for given links, i.e., 
223 	 * map where key == {@link NavPointNeighbourLink}, value == how many times bot failed to navigate through the link.
224 	 * 
225 	 * @return
226 	 */
227 	public CountIntMap<NavPointNeighbourLink> getBadLinks() {
228 		return badLinks;
229 	}
230 	
231 	//
232 	// LISTENERS
233 	//
234 
235 	public static interface ILinkRemovalListener extends EventListener {
236 		
237 		/**
238 		 * Asks whether this link can be removed from navigation graph.
239 		 * @param link
240 		 * @return
241 		 */
242 		public boolean canRemoveLink(NavPointNeighbourLink link);
243 		
244 		/**
245 		 * Reports that some link has been removed from navigation graph.
246 		 * @param link
247 		 */
248 		public void linkRemoved(NavPointNeighbourLink link);
249 		
250 	}
251 
252 	private Listeners<ILinkRemovalListener> linkRemovalListeners = new Listeners<ILinkRemovalListener>();
253 	
254 	public NavPointNeighbourLink listenerLink;
255 	
256 	public boolean listenersLinkCanRemove = true;
257 	
258 	private Listeners.ListenerNotifier<ILinkRemovalListener> canRemoveLinkNotifier = new Listeners.ListenerNotifier<ILinkRemovalListener>() {		
259 		
260 		@Override
261 		public NavPointNeighbourLink getEvent() {
262 			return listenerLink;
263 		}
264 
265 		@Override
266 		public void notify(ILinkRemovalListener listener) {
267 			listenersLinkCanRemove = listenersLinkCanRemove && listener.canRemoveLink(listenerLink);
268 		}
269 		
270 	};
271 	
272 	private Listeners.ListenerNotifier<ILinkRemovalListener> linkRemovedNotifier = new Listeners.ListenerNotifier<ILinkRemovalListener>() {
273 
274 		@Override
275 		public NavPointNeighbourLink getEvent() {
276 			return listenerLink;
277 		}
278 
279 		@Override
280 		public void notify(ILinkRemovalListener listener) {
281 			listener.linkRemoved(listenerLink);
282 		}
283 		
284 	};
285 	
286 	public void addListener(ILinkRemovalListener listener) {
287 		linkRemovalListeners.addStrongListener(listener);
288 	}
289 	
290 	public void removeListener(ILinkRemovalListener listener) {
291 		linkRemovalListeners.removeListener(listener);
292 	}
293 	
294 	public boolean isListener(ILinkRemovalListener listener) {
295 		return linkRemovalListeners.isListening(listener);
296 	}
297 	
298 }