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