1 """Store and calculate routing data"""
2
3 import os
4 import time
5 import logging
6 import array
7 import struct
8 import bz2
9
10 __author__ = "F. Ludwig"
11 __maintainer__ = "B. Henne"
12 __contact__ = "henne@dcsec.uni-hannover.de"
13 __copyright__ = "(c) 2010-2011, DCSec, Leibniz Universitaet Hannover, Germany"
14 __license__ = "GPLv3"
15
16
18 """Yields n times the value (default=0) to init an array.array.
19 @author: F. Ludwig"""
20 for i in xrange(int(n)):
21 yield value
22
23
25 """A routing node.
26 @author: F. Ludwig"""
27
29 """Inits the RoutingNode."""
30 self.id = id
31 self.neighbors = {}
32 self.ways = {}
33 self.todo = set()
34 self.worldobject = None
35
36 - def setup(self, nodes_num):
37 """Set up arrays route_next and route_dist with default values
38
39 - route_dist default = 0.
40 - route_next default = max_int = 255: a RoutingNode should never
41 have 255 neighbors! for performace (mem usage) reasons we store
42 id of the x-th sorted neighbor instead of the mosp wide node id.
43
44 @param nodes_num: number of way nodes"""
45 self.route_next = array.array('B', init_array(nodes_num, 255))
46 self.route_dist = array.array('H', init_array(nodes_num))
47
49 """Feed arrays route_next and route_dist with routing information to direct neighbors.
50
51 self is source, for all neighbors as destination: self.set_route(neighborX, via itself (neighborX), distance)
52 @param nodes_num: number of way nodes"""
53 self.n = sorted(self.neighbors.keys())
54 for node, n_dist in self.neighbors.items():
55 self.set_route(node, node, n_dist)
56
58 """Calculate some routing data for all RoutingNodes in self.todo.
59
60 For any node X in todo: set distance from any of my neighbor to X as the sum
61 of the distance from my neighbor to me plus the distance from me to X.
62 Finally: empty todo list.
63 @return: self.todo has not been empty?
64 @rtype: Boolean"""
65 if not self.todo:
66 return False
67
68 for to in self.todo:
69 next, dist = self.get_route_dist(to)
70 for n, d in self.neighbors.items():
71 n.set_route(to, self, dist+d)
72 self.todo = set()
73 return True
74
76 """Returns the next RoutingNode on the route from this routingNode to <node>
77
78 @param node: destination node
79 @return: next RoutingNode on route
80 @rtype: RoutingNode"""
81
82 node_id = node if isinstance(node, int) else node.id
83 next = self.route_next[node_id]
84 if next != 255:
85 return self.n[next]
86 else:
87 return None
88
90 """Returns the next RoutingNode and distance to it on the route from this routingNode to <node>
91
92 @param node: destination node
93 @return: 2-tupel list <next RoutingNode, distance>
94 @rtype: [RoutingNode, float]"""
95 node_id = node if isinstance(node, int) else node.id
96 dist = self.route_dist[node_id]
97 next = self.route_next[node_id]
98 if next != 255:
99 return self.n[next], dist
100 else:
101 return None, float('inf')
102
104 """Yields all distances from this RoutingNode to all nodes as 2-tuples <n, distance>."""
105 for n, dist in enumerate(self.route_dist):
106 if dist > 0:
107 yield n, self.get_route(n)
108
110 """Sets value of route_next (<next>) and route_dist (<dist>) for route from this RoutingNode to <to>
111
112 Updates a routing table entry within table calculation,
113 also marks destination as dirty for recalculation."""
114 cur_route = self.get_route_dist(to)
115 if cur_route[1] > dist:
116 to_id = to if isinstance(to, int) else to.id
117 next_pos = self.n.index(next)
118 self.route_next[to_id] = next_pos
119 self.route_dist[to_id] = dist
120 self.todo.add(to)
121
123 """Clears RoutingNode's route_dist and neighbors"""
124 self.route_dist = None
125 self.neighbors = None
126
128 """What has to be done when a Person visits this node in simulation?
129
130 Must be overwritten in simulation implementation to make it come alive.
131 @param visitor: visiting Person"""
132 pass
133
135 """String respresentation of RoutingNode."""
136 return '<RoutingNode id="%i">' % self.id
137
139 """Compares two RoutingNodes by their id."""
140 return cmp(self.id, o.id)
141
142
143 -def calc(nodes, path=None, dist=True, check=True, setup=True):
144 """Calculate routing tables
145
146 @param path: sets the base path. It is used to save a cached version of
147 the routing tables
148
149 @param dist: if set to false the distance between every node to every
150 other node is not held in memory
151
152 @param check: even if the routing tables are loaded from cache files its
153 checked if they are complete - by doing at least one
154 routing iteration. If check is false this is skipped
155 @author: F. Ludwig
156 """
157 cache = None
158 if path:
159 cache_path = path + '.routes'
160 cache_path_bz2 = cache_path + '.bz2'
161 if os.path.exists(cache_path_bz2):
162 cache = bz2.BZ2File(cache_path_bz2)
163 elif os.path.exists(cache_path):
164 cache = open(cache_path)
165
166 nodes_num = float(len(nodes))
167 if cache:
168 pass
169
170 cache_nodes_num = struct.unpack('!I', cache.read(4))[0]
171 if cache_nodes_num == len(nodes):
172 setup = False
173 for node in nodes:
174 node.route_next = array.array('B')
175 node.route_next.fromstring(cache.read(len(nodes)))
176 if dist or check:
177 node.route_dist = array.array('H')
178 node.route_dist.fromstring(cache.read(len(nodes)*2))
179
180 if setup:
181 pass
182
183 t = time.time()
184 for n in nodes:
185 n.setup(nodes_num)
186 pass
187
188
189 t = time.time()
190 for n in nodes:
191 n.setup2(nodes_num)
192 pass
193
194
195 run = True
196 changed = False
197 while check and run:
198 t = time.time()
199 run = False
200 for n in nodes:
201 if n.update(nodes_num):
202 run = True
203 changed = True
204
205 pass
206
207
208
209 if path and changed:
210 cache = bz2.BZ2File(cache_path_bz2, 'w')
211 cache.write(struct.pack('!I', len(nodes)))
212 for i, n in enumerate(nodes):
213 assert i == n.id
214 cache.write(n.route_next.tostring())
215 cache.write(n.route_dist.tostring())
216 cache.close()
217
218 if not dist:
219 for node in nodes:
220 node.cleanup()
221
222
223 -def slow_calc(nodes, path=None, dist=True, check=True, setup=True):
224 """Calculate routing tables in a slower, but memory-saving way.
225
226 This method should be used, when big maps are used.
227
228 @param path: sets the base path. It is used to save a cached version of
229 the routing tables
230
231 @param dist: if set to false the distance between every node to every
232 other node is not held in memory
233
234 @param check: even if the routing tables are loaded from cache files its
235 checked if they are complete - by doing at least one
236 routing iteration. If check is false this is skipped
237 @author: P. Tute
238 """
239 import networkx as nx
240 graph = nx.Graph()
241 cache = None
242 if path:
243 cache_path = path + '.routes'
244 cache_path_bz2 = cache_path + '.bz2'
245 if os.path.exists(cache_path_bz2):
246 cache = bz2.BZ2File(cache_path_bz2)
247 elif os.path.exists(cache_path):
248 cache = open(cache_path)
249
250 nodes_num = float(len(nodes))
251 if cache:
252 pass
253
254 cache_nodes_num = struct.unpack('!I', cache.read(4))[0]
255
256 if cache_nodes_num == len(nodes):
257 setup = False
258 for node in nodes:
259 node.route_next = array.array('B')
260 node.route_next.fromstring(cache.read(len(nodes)))
261 if dist or check:
262 node.route_dist = array.array('H')
263 node.route_dist.fromstring(cache.read(len(nodes)*2))
264
265 if setup:
266
267 graph.add_nodes_from(nodes)
268 for node in nodes:
269 for neigh in node.neighbors:
270 graph.add_edge(node, neigh, {'dist': node.neighbors[neigh]})
271