1 package cz.cuni.amis.pogamut.ut2004.agent.module.sensor.visibility;
2
3 import java.io.File;
4 import java.util.ArrayList;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Set;
8 import java.util.concurrent.ConcurrentLinkedQueue;
9 import java.util.concurrent.LinkedBlockingQueue;
10 import java.util.concurrent.RejectedExecutionException;
11 import java.util.concurrent.ThreadPoolExecutor;
12 import java.util.concurrent.TimeUnit;
13 import java.util.logging.Level;
14
15 import cz.cuni.amis.pogamut.base.agent.state.level1.IAgentStateUp;
16 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
17 import cz.cuni.amis.pogamut.base.component.bus.event.BusAwareCountDownLatch;
18 import cz.cuni.amis.pogamut.base.utils.Pogamut;
19 import cz.cuni.amis.pogamut.base.utils.logging.LogCategory;
20 import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
21 import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.visibility.model.VisibilityLocation;
22 import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.visibility.model.VisibilityMatrix;
23 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.FastTrace;
24 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.GetAllInvetories;
25 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.GetAllNavPoints;
26 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.FastTraceResponse;
27 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
28 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPointNeighbourLink;
29 import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.MapPointListObtained;
30 import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerFactory;
31 import cz.cuni.amis.pogamut.ut2004.factory.guice.remoteagent.UT2004ServerModule;
32 import cz.cuni.amis.pogamut.ut2004.server.impl.UT2004Server;
33 import cz.cuni.amis.pogamut.ut2004.utils.UT2004ServerRunner;
34 import cz.cuni.amis.utils.StopWatch;
35 import cz.cuni.amis.utils.Tuple3;
36 import cz.cuni.amis.utils.flag.FlagInteger;
37 import cz.cuni.amis.utils.maps.MapWithKeyListeners;
38 import cz.cuni.amis.utils.maps.MapWithKeyListeners.KeyCreatedEvent;
39 import cz.cuni.amis.utils.token.Token;
40 import cz.cuni.amis.utils.token.Tokens;
41
42
43
44
45
46
47
48
49
50 public class VisibilityCreator {
51
52
53
54
55 public static final int MATRIX_DENSITY = 100;
56
57
58
59
60 public static final Location SECOND_TRACE_DELTA = new Location(0, 0, 50);
61
62
63
64
65
66 public static final int THREAD_COUNT = 10;
67
68 private UT2004Server server;
69
70 private LogCategory log;
71
72 public VisibilityCreator() {
73 UT2004ServerModule serverModule = new UT2004ServerModule();
74 UT2004ServerFactory serverFactory = new UT2004ServerFactory(serverModule);
75 UT2004ServerRunner serverRunner = new UT2004ServerRunner(serverFactory);
76 this.server = (UT2004Server) serverRunner.startAgent();
77 this.log = server.getLogger().getCategory(getClass().getSimpleName());
78 this.log.setLevel(Level.INFO);
79 }
80
81 public VisibilityCreator(UT2004Server server) {
82 this.server = server;
83 this.log = server.getLogger().getCategory(getClass().getSimpleName());
84 this.log.setLevel(Level.INFO);
85 }
86
87 public UT2004Server getServer() {
88 return server;
89 }
90
91 public LogCategory getLog() {
92 return log;
93 }
94
95 public synchronized VisibilityMatrix create() {
96
97 log.info("CREATING VISIBILITY MATRIX");
98
99 StopWatch watch = new StopWatch();
100
101 checkServer();
102
103 log.info("Time: " + watch.checkStr());
104
105 MapPointListObtained mapPoints = obtainMapPoints();
106
107 log.info("Time: " + watch.checkStr());
108
109 List<Tuple3<Location, NavPoint, NavPointNeighbourLink>> locations = generateLocations(mapPoints);
110
111 log.info("Time: " + watch.checkStr());
112
113 VisibilityMatrix matrix = precreateMatrix(locations);
114
115 log.info("Time: " + watch.checkStr());
116
117 fillMatrix(matrix);
118
119 log.info("Time: " + watch.checkStr());
120
121 log.info("Visibility stats: " + matrix.getVisiblePairCount() + " / " + matrix.getPairCount() + " visible pairs");
122
123 return matrix;
124 }
125
126 public VisibilityMatrix createAndSave(File targetDirectory) {
127 VisibilityMatrix matrix = create();
128
129 log.info("SAVING VISIBILITY MATRIX");
130
131 StopWatch watch = new StopWatch();
132
133 save(matrix, targetDirectory);
134
135 log.info("Time: " + watch.checkStr());
136
137 return matrix;
138 }
139
140
141
142
143
144 private void checkServer() {
145 log.info("Checking server...");
146 if (!server.inState(IAgentStateUp.class)){
147 log.info("Server is not running, starting server...");
148 server.start();
149 log.info("Server started.");
150 } else {
151 log.info("Server running.");
152 }
153 log.info("Map name: " + server.getMapName());
154 }
155
156
157
158
159
160 private MapPointListObtained mapPointsTemp;
161 private BusAwareCountDownLatch mapPointsLatch;
162
163 private IWorldEventListener<MapPointListObtained> mapPointsListener = new IWorldEventListener<MapPointListObtained>() {
164
165 @Override
166 public void notify(MapPointListObtained event) {
167 VisibilityCreator.this.mapPointsTemp = event;
168 server.getWorldView().removeEventListener(MapPointListObtained.class, this);
169 mapPointsLatch.countDown();
170 }
171
172 };
173
174 private MapPointListObtained obtainMapPoints() {
175 log.info("Getting navpoints...");
176
177 mapPointsLatch = new BusAwareCountDownLatch(1, server.getEventBus(), server.getWorldView());
178 server.getWorldView().addEventListener(MapPointListObtained.class, mapPointsListener);
179 server.getAct().act(new GetAllNavPoints());
180 server.getAct().act(new GetAllInvetories());
181
182 mapPointsLatch.await();
183 mapPointsLatch = null;
184
185 log.info("Navpoints obtained, total " + mapPointsTemp.getNavPoints().size() + " navpoints in map.");
186
187 return mapPointsTemp;
188 }
189
190
191
192
193
194 private List<Tuple3<Location, NavPoint, NavPointNeighbourLink>> generateLocations(MapPointListObtained mapPoints) {
195 log.info("Generating visibility locations...");
196
197 if (mapPoints.getNavPoints() == null || mapPoints.getNavPoints().size() == 0) {
198 throw new RuntimeException("No navpoints in map?");
199 }
200
201 Set<NavPoint> finished = new HashSet<NavPoint>();
202
203 Set<NavPoint> pending = new HashSet<NavPoint>();
204 pending.addAll(mapPoints.getNavPoints().values());
205
206 List<Tuple3<Location, NavPoint, NavPointNeighbourLink>> result = new ArrayList<Tuple3<Location, NavPoint, NavPointNeighbourLink>>(mapPoints.getNavPoints().size()*3);
207
208 while (pending.size() > 0) {
209 NavPoint from = pending.iterator().next();
210 pending.remove(from);
211
212
213 result.add(new Tuple3<Location, NavPoint, NavPointNeighbourLink>(from.getLocation(), from, null));
214
215 for (NavPointNeighbourLink link : from.getOutgoingEdges().values()) {
216 NavPoint to = link.getToNavPoint();
217 if (finished.contains(to)) continue;
218
219 List<Location> locations = getLocationsBetween(from, to);
220 for (Location location : locations) {
221 result.add(new Tuple3<Location, NavPoint, NavPointNeighbourLink>(location, null, link));
222 }
223 }
224
225 finished.add(from);
226 }
227
228 log.info("Done, generated " + (result.size()) + " unique locations for visibility matrix.");
229 log.info("Matrix " + result.size() + "x" + result.size() + " == " + (result.size()*result.size()) + " bits == " + (result.size()*result.size()/8) + " bytes.");
230
231 return result;
232 }
233
234
235
236
237
238
239
240 private List<Location> getLocationsBetween(NavPoint from, NavPoint to) {
241 double distance = from.getLocation().getDistance(to.getLocation());
242 int parts = ((int)Math.round(distance)) / MATRIX_DENSITY;
243
244 if (parts <= 0) {
245
246 return new ArrayList<Location>(0);
247 }
248
249 double oneLength = distance / ((double)parts);
250 Location vector = to.getLocation().sub(from.getLocation()).getNormalized().scale(oneLength);
251
252 List<Location> result = new ArrayList<Location>(parts-1);
253
254
255
256
257
258 for (int i = 1; i < parts; ++i) {
259 result.add(from.getLocation().add(vector.scale(i)));
260 }
261
262
263
264
265 return result;
266 }
267
268
269
270
271
272 private VisibilityMatrix precreateMatrix(List<Tuple3<Location, NavPoint, NavPointNeighbourLink>> locations) {
273
274 log.info("Pre-creating visibility matrix, filling visibility-locations...");
275
276 VisibilityMatrix result = new VisibilityMatrix(server.getMapName(), locations.size());
277
278 int i = 0;
279 for (Tuple3<Location, NavPoint, NavPointNeighbourLink> location : locations) {
280 Location loc = location.getFirst();
281 NavPoint np = location.getSecond();
282 NavPointNeighbourLink link = location.getThird();
283
284 VisibilityLocation vLoc = new VisibilityLocation();
285 vLoc.x = loc.x;
286 vLoc.y = loc.y;
287 vLoc.z = loc.z;
288
289 if (np != null) {
290 vLoc.navPoint = np;
291 vLoc.navPoint1Id = getNavPointId(server.getMapName(), np);
292 } else
293 if (link != null) {
294 vLoc.link = link;
295 vLoc.navPoint1Id = getNavPointId(server.getMapName(), link.getFromNavPoint());
296 vLoc.navPoint2Id = getNavPointId(server.getMapName(), link.getToNavPoint());
297 } else {
298 throw new RuntimeException("Tuple3 has neither NavPoint nor Link information, invalid.");
299 }
300
301 result.getLocations().put(i, vLoc);
302
303 ++i;
304 }
305
306 log.info("Visibility matrix pre-created.");
307
308 return result;
309 }
310
311 private String getNavPointId(String mapName, NavPoint np) {
312 String id = np.getId().getStringId();
313 String result = id.substring(mapName.length()+1);
314 return result;
315 }
316
317
318
319
320
321 private int totalRaycast;
322 private int jobsGenerated;
323 private FlagInteger jobsCompleted = new FlagInteger(0);
324 private MapWithKeyListeners<Token, FastTraceResponse> fastTraceResponses;
325 private VisibilityMatrix matrix;
326
327 private void fillMatrix(VisibilityMatrix matrix) {
328
329 log.info("Gathering visibility information...");
330
331 this.matrix = matrix;
332 jobsGenerated = 0;
333 jobsCompleted.setFlag(0);
334 fastTraceResponses = new MapWithKeyListeners<Token, FastTraceResponse>();
335
336 totalRaycast = (matrix.getLocations().size()) * (matrix.getLocations().size()-1) / 2;
337
338 log.info("Estimated number of raycasts to perform: " + totalRaycast);
339
340 log.info("Registering FastTraceResponse listener...");
341 server.getWorldView().addEventListener(FastTraceResponse.class, fastTraceListener);
342
343 try {
344
345 log.info("Starting thread pool executor...");
346
347 ThreadPoolExecutor executor = new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1000));
348
349 try {
350 log.info("Generating jobs...");
351
352 int locations = matrix.getLocations().size();
353 for (int i = 0; i < locations; ++i) {
354
355 synchronized(matrix) {
356 matrix.getMatrix().set(i, i);
357 }
358
359 for (int j = i+1; j < locations; ++j) {
360 TraceJob job = new TraceJob(i, matrix.getLocation(i), j, matrix.getLocation(j));
361 ++jobsGenerated;
362 if (jobsGenerated % 1000 == 0) {
363 log.info("Generated " + jobsGenerated + " / " + totalRaycast + " jobs generated...");
364 }
365
366
367 while (true) {
368 try {
369 executor.execute(job);
370
371 break;
372 } catch (RejectedExecutionException e1) {
373
374 try {
375 Thread.sleep(5000);
376 } catch (InterruptedException e2) {
377 throw new RuntimeException("Interrupted while asleep.", e2);
378 }
379 }
380 }
381 }
382 }
383
384 log.info("Generated all " + jobsGenerated + " jobs, waiting their completion...");
385
386 jobsCompleted.waitFor(new Integer[]{jobsGenerated});
387
388 log.info("All " + jobsGenerated + " jobs finished!");
389
390 } finally {
391 executor.shutdownNow();
392 }
393
394 } finally {
395 try {
396 log.info("Removing FastTraceResponse listener...");
397 } finally {
398 try {
399 server.getWorldView().removeEventListener(FastTraceResponse.class, fastTraceListener);
400 } finally {
401 this.matrix = null;
402 }
403 }
404 }
405
406 log.info("Visibility information gathered.");
407 }
408
409 private class TraceJob implements Runnable {
410
411 private VisibilityLocation from;
412 private VisibilityLocation to;
413 private int indexFrom;
414 private int indexTo;
415
416 public TraceJob(int indexFrom, VisibilityLocation from, int indexTo, VisibilityLocation to) {
417 this.indexFrom = indexFrom;
418 this.from = from;
419 this.indexTo = indexTo;
420 this.to = to;
421 }
422
423 @Override
424 public void run() {
425
426 synchronized(matrix) {
427 matrix.getMatrix().unset(indexFrom, indexTo);
428 matrix.getMatrix().unset(indexTo, indexFrom);
429 }
430
431 final Token id1 = getNextFastTraceId();
432 final Token id2 = getNextFastTraceId();
433
434 final BusAwareCountDownLatch latch = new BusAwareCountDownLatch(2, server.getEventBus(), server.getWorldView());
435
436 final FastTrace fastTrace1 = new FastTrace().setId(id1.getToken()).setFrom(from.getLocation()).setTo(to.getLocation());
437 final FastTrace fastTrace2 = new FastTrace().setId(id2.getToken()).setFrom(from.getLocation().add(SECOND_TRACE_DELTA)).setTo(to.getLocation().add(SECOND_TRACE_DELTA));
438
439 MapWithKeyListeners.IKeyCreatedListener<Token, FastTraceResponse> listener = new MapWithKeyListeners.IKeyCreatedListener<Token, FastTraceResponse>() {
440 @Override
441 public void notify(KeyCreatedEvent<Token, FastTraceResponse> event) {
442 synchronized(fastTraceResponses) {
443 fastTraceResponses.remove(event.getKey());
444 }
445 if (!event.getValue().isResult()) {
446 synchronized(matrix) {
447
448 matrix.getMatrix().set(indexFrom, indexTo);
449 matrix.getMatrix().set(indexTo, indexFrom);
450 }
451 while (latch.getCount() > 0) latch.countDown();
452 } else {
453 latch.countDown();
454 if (latch.getCount() > 0) {
455
456 server.getAct().act(fastTrace2);
457 }
458 }
459 }
460 };
461
462 fastTraceResponses.addWeakListener(id1, listener);
463 fastTraceResponses.addWeakListener(id2, listener);
464
465
466 server.getAct().act(fastTrace1);
467
468 latch.await();
469
470 fastTraceResponses.removeListener(id1, listener);
471 fastTraceResponses.removeListener(id2, listener);
472
473 returnId(id1);
474 returnId(id2);
475
476 jobsCompleted.increment(1);
477 int num = jobsCompleted.getFlag();
478 if (num % 50 == 0) {
479 log.info("Raycast " + num + " / " + totalRaycast);
480 }
481 if (num % 1000 == 0) {
482 log.info("Existing IDs: " + nextId);
483 }
484 }
485
486 }
487
488 private IWorldEventListener<FastTraceResponse> fastTraceListener = new IWorldEventListener<FastTraceResponse>() {
489
490 @Override
491 public void notify(FastTraceResponse event) {
492 fastTraceResponses.put(Tokens.get(event.getId()), event);
493 }
494
495 };
496
497 private Object nextIdMutex = new Object();
498 private int nextId = 0;
499 private ConcurrentLinkedQueue<Token> availableTokens = new ConcurrentLinkedQueue<Token>();
500
501 private Token getNextFastTraceId() {
502 try {
503 if (availableTokens.size() > 0) {
504 return availableTokens.remove();
505 }
506 } catch (Exception e) {
507 }
508 int myId;
509 synchronized(nextIdMutex) {
510 myId = nextId++;
511 }
512 return Tokens.get("FTID-" + myId);
513 }
514
515 private void returnId(Token token) {
516 availableTokens.add(token);
517 }
518
519
520
521
522
523 private void save(VisibilityMatrix matrix, File targetDirectory) {
524
525 log.info("Saving visibility matrix into: " + targetDirectory.getAbsolutePath());
526
527 if (targetDirectory.exists()) {
528 if (targetDirectory.isFile()) {
529 throw new RuntimeException("'targetDirectory' points to " + targetDirectory.getAbsolutePath() + " which is FILE not DIRECTORY");
530 } else
531 if (!targetDirectory.isDirectory()) {
532 throw new RuntimeException("'targetDirectory' points to " + targetDirectory.getAbsolutePath() + " which is not DIRECTORY");
533 }
534 } else {
535 if (!targetDirectory.mkdirs()) {
536 throw new RuntimeException("Failed to create 'targetDirectory' -> " + targetDirectory.getAbsolutePath());
537 }
538 }
539
540 matrix.save(targetDirectory);
541
542 log.info("Visibility matrix saved.");
543 }
544
545
546
547
548
549
550
551
552
553
554 public static void main(String[] args) {
555 VisibilityCreator creator = new VisibilityCreator();
556 try {
557 creator.createAndSave(new File("."));
558 } finally {
559 try {
560 creator.getServer().stop();
561 } finally {
562 Pogamut.getPlatform().close();
563 }
564 }
565 }
566
567 }