1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 import sys
41 import os
42 import random
43 import operator
44 from math import *
45 from numpy import *
46
47 import seaModels
48 import shipModels
49 import state
50 import agents
51 import simulationEnvironment
52 import factories
53
54
55 from geometry import *
56
58 return str + ' [Default: %default]'
59
61 """
62 This functions validates we didn't mistakenly chosen options
63 that don't work with each other.
64 If options are verified we just return to the the program
65 execution. If the options are incorrect, we gracefully exit.
66 """
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 return
108
109 print 'Illegal options combination - exiting...'
110 sys.exit()
111
112
113
114
115
119
121 """
122 Processes the command used to run the simulation from the command line.
123 """
124 from optparse import OptionParser
125 usageStr = """
126 USAGE: python main.py <options>
127 """
128 parser = OptionParser(usageStr)
129
130 parser.add_option('-q', '--quietTextGraphics', action='store_true', dest='quietGraphics',
131 help=default('Generate minimal output and no graphics'), default=False )
132 parser.add_option('-n', '--numSteps', dest='numSteps', type='int',
133 help=default("Number of steps to simulate"), default=10000)
134 parser.add_option('-s', '--speed', dest='speed', type='int',
135 help=default("frames per second in GUI mode (each frame is one simulated second)"), default=60)
136 parser.add_option('-i', '--inputFilesDir', dest='inputFilesDir',
137 help=default('the directory in which input files are searched for'),
138 metavar='DIR', default='input_files')
139 parser.add_option('-f', '--taskFile', dest='taskFile',
140 help=default('A task-definition-language file. The file is searched for under the input files directory (specified by the flag -i)'),
141 default='task_multiple_ships_tracking_multiple_targets.py')
142 parser.add_option('-r', '--randomSeed', dest='randomSeed', type='int',
143 help=default('reloads a random seed - for simulation replay'), default=None)
144 parser.add_option('-d', '--debug', action='store_true', dest='debug',
145 help=default('turns on debug prints'),
146 default=False)
147
148
149
150
151
152
153 options, other = parser.parse_args()
154 if len(other) != 0:
155 raise Exception('Command line input not understood: ' + other)
156 args = dict()
157
158
159
160 random.seed(options.randomSeed)
161
162
163 if options.quietGraphics:
164 args['withDisplay'] = False
165 else:
166 args['withDisplay'] = True
167
168
169 args['numSteps'] = options.numSteps
170 args['speed'] = options.speed
171 args['inputFilesDir'] = options.inputFilesDir
172 args['taskFile'] = options.taskFile
173 args['debug'] = options.debug
174
175 validateOptionCombinations(args)
176
177 return args
178
179
180
181
182
183
184
185
186
188
189 exec(compile(open(filePath).read(), filePath, 'exec'))
190 return wind
191
193
194 exec(compile(open(filePath).read(), filePath, 'exec'))
195 return waterCurrents
196
198
199 exec(compile(open(filePath).read(), filePath, 'exec'))
200 return waves
201
203
204 exec(compile(open(filePath).read(), filePath, 'exec'))
205 return obstacles
206
207
208
209
210
212 """
213 Write the simulation results.
214
215 TODO: this function needs to be further organized
216 once we have a better picture regarding what
217 types of results are usually needed.
218 Also the function arguments should be revised as a part
219 of the function reorganization.
220
221 @type resultsType: string
222 @param resultsType: a string identifier for the type of result to write
223
224 @type resultsType: SimulationEnvironment
225 @param resultsType: The simulation environment, used to extract data from
226
227 @type patrolPoints: array of (x,y) tuples
228 @param patrolPoints: All the points that participate in the patrol. This is required when writing data for heuristicDivide algorithm, and might be removed at some point.
229 """
230
231
232
233 if resultsType == 'visit_times':
234
235 f = open(resultsType + '.res', 'w')
236
237
238 if len(env.visitTimes.values()) >= 1 and min([len(l) for l in env.visitTimes.values()]) >= 2:
239
240 point2freq = dict([(point, mean([l[i] - l[i-1] for i in range(1,len(l))])) for point, l in env.visitTimes.items() ])
241 if len(point2freq) > 0:
242 worstCaseFreq1 = max(point2freq.values())
243 f.write('WORSTCASE FREQ: ' + str(worstCaseFreq1) + '\n')
244 f.write('patrolPoints=' + str(patrolPoints) + '\n')
245 f.write('visitTimes=' + str(env.visitTimes) + '\n')
246
247 f.close()
248
249 elif resultsType == 'edgeGraphData':
250
251 f = open(resultsType + '.py', 'w')
252 f.write('patrolPoints=' + str(patrolPoints) + '\n')
253 f.write('edgeLengths=' + str(env.edgeLengths) + '\n')
254 f.close()
255
256
257
258
259
260
261
262
263 -def run(withDisplay, inputFilesDir, taskFile, numSteps, speed, debug):
264 """
265 The main flow of the application. The arguments are generated by command line options, or their defaults.
266
267 @type withDisplay: boolean
268 @param withDisplay: Tells whether to run in a GUI mode or in non-GUI mode
269
270 @type inputFilesDir: string
271 @param inputFilesDir: The directory in which the input files are
272
273 @type taskFile: string
274 @param taskFile: A task-definition-language file
275
276 @type numSteps: int
277 @param numSteps: Number of steps to simulate
278
279 @type speed: int
280 @param speed: frames-per-second (each frame is one simulated second)
281 """
282
283 origstdout = sys.stdout
284 origstderr = sys.stderr
285 if not debug:
286 sys.stdout = NullDevice()
287
288
289
290
291
292
293 windFile = 'wind.py'
294 waterFile = 'waterCurrents.py'
295 wavesFile = 'waves.py'
296 obstaclesFile = 'obstacles.py'
297 shipType = 'basic'
298 strategy = 'staticpatrol'
299 worldModel = 'complete'
300 rulesOfTheSea = True
301 resultsType = 'edgeGraphData'
302
303
304
305
306
307 pathToTaskFile = os.path.join(inputFilesDir, taskFile)
308 exec(compile(open(pathToTaskFile).read(), pathToTaskFile, 'exec'))
309
310
311
312
313
314
315 try:
316 task
317 except Exception as e:
318 print >> sys.stderr, '-E- Exception Raised:', e
319 sys.exit(1)
320 if task == 'TASK_STATIC_PATROL':
321 try:
322
323 shipInitialStates
324 patrolPoints
325 paths
326 except Exception as e:
327 print >> sys.stderr, '-E- Exception Raised:', e
328 sys.exit(1)
329
330 strategy = 'staticpatrol'
331
332 elif task == 'TASK_PATROL_WITH_ONLINE_ASSIGNMENT':
333 try:
334
335 patrolPoints
336 edgeLengths
337 shipInitialStates
338 roles
339 if len(shipInitialStates) != len(roles):
340 raise Exception("shipInitialStates and roles must be at the same length")
341 except Exception as e:
342 print >> sys.stderr, '-E- Exception Raised:', e
343 sys.exit(1)
344
345 strategy = 'coordinatedpatrol'
346
347 elif task == 'TASK_TARGET_TRACKING':
348 try:
349
350 roles
351 shipInitialStates
352 patrolPoints
353 paths
354 trackingDistance
355 positionOffsets
356 if len(shipInitialStates) != len(roles):
357 raise Exception("shipInitialStates and roles must be at the same length")
358 except Exception as e:
359 print >> sys.stderr, '-E- Exception Raised:', e
360 sys.exit(1)
361
362 strategy = 'targettracking'
363
364 else:
365 raise Exception('Unknown task type')
366
367 try:
368
369 shipInitialStates
370 strategy
371 except Exception as e:
372 print >> sys.stderr, '-E- Exception Raised:', e
373 sys.exit(1)
374
375
376
377
378
379
380 numShips = len(shipInitialStates)
381
382
383
384 if shipType == 'basic':
385 shipFactory = factories.BasicShipFactory()
386 else:
387 raise Exception('Unknown ship type: ' + shipType)
388 ships = [shipFactory.create() for i in range(numShips)]
389
390
391
392
393
394
395
396
397
398 if worldModel == 'complete':
399 worldModelFactory = factories.CompleteWorldModelFactory()
400 else:
401 raise Exception('Unknown world model type: ' + worldModel)
402
403
404 if strategy == 'circling':
405 strategyFactories = [factories.CirclingStrategyFactory() for i in range(len(ships))]
406 elif strategy == 'rrt':
407 strategyFactories = [factories.RRTStrategyFactory(paths, rulesOfTheSea) for i in range(len(ships))]
408 elif strategy == 'pid':
409 strategyFactories = [factories.PIDStrategyFactory(rulesOfTheSea) for i in range(len(ships))]
410 elif strategy == 'measuredist':
411 strategyFactories = [factories.MeasureEdgeLengthsFactory() for i in range(len(ships))]
412 elif strategy == 'staticpatrol':
413 strategyFactories = [factories.AgentStaticPatrolStrategyFactory(paths, rulesOfTheSea) for i in range(len(ships))]
414 elif strategy == 'coordinatedpatrol':
415
416 strategyFactories = []
417 for role in roles:
418 if role == 'originalPatroller':
419 strategyFactories.append(
420 factories.AgentCoordinatedPatrolStrategyFactory(patrolPoints,
421 edgeLengths, rulesOfTheSea))
422 elif role == 'joinsLater':
423 strategyFactories.append(
424 factories.AgentJoiningPatrolStrategyFactory(patrolPoints,
425 edgeLengths, rulesOfTheSea))
426 else:
427 raise Exception('Unknown ship role: ' + role)
428 elif strategy == 'targettracking':
429 trackedShipIndices = [i for i in range(len(roles)) if roles[i] == 'tracked']
430 trackingShipIndices = [i for i in range(len(roles)) if roles[i] == 'tracker']
431 if len(trackingShipIndices) != len(positionOffsets):
432 raise Exception('Number of tracking ships must equal to the length of positionOffsets')
433 if len(trackedShipIndices) != len(paths):
434 raise Exception('Number of tracked ships must equal to the number of paths')
435
436 strategyFactories = []
437 for role in roles:
438 if role == 'tracked':
439 strategyFactories.append(
440 factories.AgentStaticPatrolStrategyFactory(paths, rulesOfTheSea))
441 elif role == 'tracker':
442 strategyFactories.append(
443 factories.AgentTrackingPatrolStrategyFactory(trackedShipIndices,
444 trackingShipIndices, trackingDistance, positionOffsets, rulesOfTheSea))
445 else:
446 raise Exception('Unknown ship role: ' + role)
447 else:
448 raise Exception('Unknown decision making strategy: ' + strategy)
449
450
451 shipAgents = [agents.Agent( worldModel = worldModelFactory.create(shipIndex=i),
452 strategy = strategyFactories[i].create(agentIndex=i) )
453 for i,s in enumerate(ships)]
454 print 'shipAgents', len(shipAgents)
455
456
457
458
459
460
461 wind = initWindFromFile(os.path.join(inputFilesDir, windFile))
462 waterCurrents = initWaterCurrentsFromFile(os.path.join(inputFilesDir, waterFile))
463 waves = initWavesFromFile(os.path.join(inputFilesDir, wavesFile))
464 obstacles = initObstaclesFromFile(os.path.join(inputFilesDir, obstaclesFile))
465 sea = seaModels.Sea(wind, waterCurrents, waves, obstacles)
466
467
468
469
470
471
472 initialState = state.State( sea, ships, shipInitialStates )
473
474
475 env = simulationEnvironment.SimulationEnvironment( initialState, shipAgents )
476
477
478 import mainGUI
479 if withDisplay:
480 mainGUI.runGui(env, shipType, worldModel, strategy, numSteps, speed)
481 else:
482 env.run(numSteps)
483
484
485 writeResults(resultsType, env, patrolPoints)
486
487 sys.stdout = origstdout
488
489
490
491
492 if __name__ == '__main__':
493 """
494 The main function called when running the simulation
495
496 > python main.py
497
498 See the usage string for more details.
499
500 > python main.py --help
501 """
502 args = readCommand( sys.argv[1:] )
503 print 'Running simulation with the following options:'
504 for k, v in sorted(args.items()):
505 print k, '=', v
506 run( **args )
507