ULP-ActivitySupport – blob
1 #!/usr/bin/env python3
3 import copy
4 import json
5 import os
6 import random
7 import sys
9 from flask import Flask, escape, json as flask_json, jsonify, render_template, request, send_from_directory
11 from data import Position, Intent, User, SmartUrbanObject, Context
12 from routing import RoutingEngine
13 from simulator import ContextSimulator
15 # ULE backend connector - disabled for now
16 # from iot_service import IoTService
18 # UBW micro info radiator connector
19 from ubw_info_radiator_service import MicroInfoRadiatorService
22 class DataSerializer(json.JSONEncoder):
23 def default(self, o):
24 if hasattr(o, 'toJSON'):
25 return o.toJSON()
26 return o.__dict__
29 data = {
30 'context': {},
31 'routing': {}
32 }
34 app = Flask(__name__)
35 app.json_encoder = DataSerializer
37 services = {
38 'iot': MicroInfoRadiatorService()
39 }
42 @app.route('/')
43 def index():
44 return '', 200
46 @app.route('/assets/<name>.png')
47 def get_asset(name):
48 return send_from_directory(os.path.join(app.root_path, 'assets'),
49 name + '.png', mimetype='image/png')
51 @app.route('/<name>.js')
52 def get_script(name):
53 return send_from_directory(os.path.join(app.root_path, 'assets'),
54 name + '.js', mimetype='text/javascript')
56 @app.route('/view/<name>')
57 def render_view(name):
58 return render_template('view.html', context=name)
60 @app.route('/context/<id>')
61 def get_context(id):
62 context = data['context'].get(id)
63 if context is None:
64 return 'Invalid context ID', 401
65 routing = data['routing'].get(id)
66 result = {
67 'context': context,
68 'transient': {
69 'suo_vicinity': {},
70 'suo_on_route': {},
71 'direct_control': [],
72 'paused': [],
73 'suo_state': {},
74 }
75 }
76 for user_id in context.user:
77 suos_on_route = {}
78 if routing is not None:
79 suos_on_route = routing.get_suos_on_route(user_id)
80 for suo_id in context.suo:
81 if context.suo[suo_id].is_user_in_range(context.user[user_id]):
82 if user_id not in result['transient']['suo_vicinity']:
83 result['transient']['suo_vicinity'][user_id] = []
84 result['transient']['suo_vicinity'][user_id].append(suo_id)
85 if suo_id in suos_on_route:
86 del suos_on_route[suo_id]
87 if len(suos_on_route.keys()) > 0:
88 result['transient']['suo_on_route'][user_id] = suos_on_route
89 user = context.user[user_id]
90 if hasattr(user, 'original_intent'):
91 result['transient']['direct_control'].append(user_id)
92 if hasattr(user, 'paused') and user.paused:
93 result['transient']['paused'].append(user_id)
94 for suo_id in context.suo:
95 w = [255, 255, 255]
96 r = [255, 0, 0]
97 b = [0, 0, 255]
98 if suo_id == 'micro-0':
99 result['transient']['suo_state'][suo_id] = {}
100 result['transient']['suo_state'][suo_id]['display'] = [
101 r,r,r,r,r,r,r,r,
102 r,r,r,r,r,r,r,r,
103 r,r,r,r,r,r,r,r,
104 r,r,r,r,r,r,r,r,
105 r,r,r,r,r,r,r,r,
106 r,r,r,r,r,r,r,r,
107 r,r,r,r,r,r,r,r,
108 r,r,r,r,r,r,r,r,
109 ]
110 if suo_id == 'micro-2':
111 result['transient']['suo_state'][suo_id] = {}
112 result['transient']['suo_state'][suo_id]['display'] = [
113 b,b,b,w,w,b,b,b,
114 b,b,w,w,b,b,b,b,
115 b,w,w,b,b,b,b,b,
116 w,w,w,w,w,w,w,w,
117 w,w,w,w,w,w,w,w,
118 b,w,w,b,b,b,b,b,
119 b,b,w,w,b,b,b,b,
120 b,b,b,w,w,b,b,b,
121 ]
122 return jsonify(result)
124 @app.route('/routing/<id>')
125 def get_routing_network(id):
126 routing_engine = data['routing'].get(id)
127 if routing_engine is None:
128 return 'Invalid routing context ID', 401
129 result = copy.deepcopy(routing_engine.get_network())
130 return jsonify(result)
132 @app.route('/routing/<id>/route_nodes/<nodeA>/<nodeB>')
133 def get_route_nodes(id, nodeA, nodeB):
134 routing_engine = data['routing'].get(id)
135 if routing_engine is None:
136 return 'Invalid routing context ID', 401
137 result = routing_engine.route_between_nodes(nodeA, nodeB)
138 return jsonify(result)
140 @app.route('/routing/<id>/route/<coordsA>/<coordsB>')
141 def get_route_coords(id, coordsA, coordsB):
142 routing_engine = data['routing'].get(id)
143 if routing_engine is None:
144 return 'Invalid routing context ID', 401
145 coordsA = coordsA.split(',')
146 coordsA = { 'x': coordsA[0], 'y': coordsA[1] }
147 coordsB = coordsB.split(',')
148 coordsB = { 'x': coordsB[0], 'y': coordsB[1] }
149 result = routing_engine.route_between_coords(coordsA, coordsB)
150 return jsonify(result)
152 @app.route('/routing/<id>/route/<uids>')
153 def get_route_for_user(id, uids):
154 routing_engine = data['routing'].get(id)
155 if routing_engine is None:
156 return 'Invalid routing context ID', 401
157 result = {}
158 for uid in uids.split(','):
159 result[uid] = routing_engine.get_user_route(uid)
160 return jsonify(result)
162 @app.route('/<cid>/user/<uid>/update_position')
163 def update_position(cid, uid):
164 context = data['context'].get(cid)
165 if context is None:
166 return 'Invalid context ID', 401
167 user = context.user.get(uid)
168 if user is None:
169 return 'Invalid user ID', 401
170 lat = request.args.get('lat')
171 lon = request.args.get('lon')
172 if lat is None or lon is None:
173 return 'Must provide lat and lon parameters', 401
174 user.set_position(Position(float(lat), float(lon)))
175 return jsonify(user)
177 @app.route('/<cid>/admin/<command>/<param>')
178 def admin_command(cid, command, param):
179 context = data['context'].get(cid)
180 if context is None:
181 return 'Invalid context ID', 401
182 if command not in ['enable_direct_control', 'disable_direct_control', 'enable_pause', 'disable_pause', 'set_goal', 'set_mobility']:
183 return 'Invalid command', 401
184 else:
185 if ';' in param:
186 user = context.user.get(param.split(';')[0])
187 else:
188 user = context.user.get(param)
189 if user is None:
190 return 'Invalid user ID', 401
191 if not user.is_virtual:
192 return 'User is not virtual - cannot be controlled', 401
193 if command == 'enable_direct_control':
194 user = context.user[param]
195 if hasattr(user, 'original_intent'):
196 return 'User already under direct control', 401
197 user.original_intent = user.intent
198 user.intent = {}
199 if command == 'disable_direct_control':
200 user = context.user[param]
201 if not hasattr(user, 'original_intent'):
202 return 'User was not under direct control', 401
203 user.intent = user.original_intent
204 del user.original_intent
205 if command == 'enable_pause':
206 user = context.user[param]
207 if hasattr(user, 'paused') and user.paused:
208 return 'User already paused', 401
209 user.paused = True
210 if command == 'disable_pause':
211 user = context.user[param]
212 if not hasattr(user, 'paused') or not user.paused:
213 return 'User was not paused', 401
214 del user.paused
215 if command == 'set_goal':
216 uid, goal = param.split(';')
217 user = context.user[uid]
218 if not hasattr(user, 'original_intent'):
219 return 'User is not under direct control', 401
220 x, y = goal.split(',');
221 user.intent = { 1: Intent(1, Position( x = float(x), y = float(y))) }
222 if command == 'set_mobility':
223 uid, mobility = param.split(';')
224 user = context.user[uid]
225 if mobility not in ['foot', 'walker', 'wheelchair', 'scooter']:
226 return 'Invalid mobility', 401
227 user.mobility = mobility
228 return jsonify('success')
230 @app.route('/update_suo_list')
231 def update_suo_list(context = 'demo'):
232 devices = services['iot'].get_devices()
233 if devices is None: # connection error or something else we can't fix
234 return jsonify(None)
235 for device in devices:
236 if isinstance(services['iot'], MicroInfoRadiatorService):
237 new_suo = SmartUrbanObject(device['_id']['$oid'], 'microinforadiator')
238 del device['_id']
239 lat, lon = device['location']['coordinates']
240 del device['location']
241 else:
242 new_suo = SmartUrbanObject(device['id'], device['type'])
243 lat, lon = 0, 0 # TODO properly set position
244 new_suo.set_position(Position(lat = float(lat), lon = float(lon)))
245 for attrib in device:
246 setattr(new_suo, attrib, device[attrib])
247 data['context'][context].add_suo(new_suo)
248 return jsonify(data['context'][context])
251 #
252 # --- dummy profile service ---
253 #
255 users = {
256 '17318505-bdf4-4cb0-b918-5e073de20979': {
257 'first_name': 'Margot',
258 'last_name': 'Nowak',
259 'birth_year': 1938,
260 'bluetooth_ids': ['d6:ee:d4:16:ed:fc'],
261 'color_preference': [255, 0, 0],
262 },
263 '29673db0-35f6-45af-900d-edc15e65c831': {
264 'first_name': 'Emrah',
265 'last_name': 'Dogan',
266 'birth_year': 1927,
267 'bluetooth_ids': ['f6:be:90:32:3c:5e', 'eb:e3:0f:49:84:d2'],
268 'color_preference': [0, 0, 255],
269 },
270 }
272 @app.route('/users')
273 def get_users():
274 return jsonify(users)
276 @app.route('/user/<id>')
277 def get_user(id):
278 return jsonify(users.get(id))
280 @app.route('/user/by-bluetooth/<btid>')
281 def get_user_by_bluetooth(btid):
282 btid = btid.lower()
283 result = None
284 for user in users:
285 if btid in users[user]['bluetooth_ids']:
286 result = users[user]
287 return jsonify(result)
290 #
291 # Nur zum Testen, kommt irgendwann weg! :)
292 #
293 def setup_demo_world():
294 world = Context('demo', 'Scooter-Park')
295 world.set_boundaries(54.135, 54.135)
296 routing_engine = RoutingEngine(world)
297 i = 0
298 for user_id in users:
299 user = User(user_id, users[user_id]['first_name'] + ' ' + users[user_id]['last_name'])
300 user.color_preference = users[user_id]['color_preference']
301 user.mobility = 'foot'
302 user.is_virtual = True
303 if i == 0:
304 pos = Position(x = 6.0, y = 41.0)
305 rotation = 0
306 user.add_intent(Intent(1, Position(x = 34.2, y = 44.6)))
307 user.add_intent(Intent(2, Position(x = 41.3, y = 11.2)))
308 user.add_intent(Intent(3, Position(x = 22.6, y = 11.2)))
309 user.add_intent(Intent(4, Position(x = 6.3, y = 18.8)))
310 user.add_intent(Intent(5, Position(x = 6.0, y = 41.0)))
311 else:
312 pos = Position(x = 22.1, y = 20.4)
313 rotation = 270
314 user.add_intent(Intent(1, Position(x = 38.0, y = 33.0)))
315 user.add_intent(Intent(2, Position(x = 13.8, y = 29.7)))
316 user.add_intent(Intent(3, Position(x = 22.1, y = 20.4)))
317 user.set_position(pos)
318 user.set_rotation(rotation)
319 world.add_user(user)
320 i += 1
321 data['context']['demo'] = world
322 data['routing']['demo'] = routing_engine
323 for i in range(3):
324 new_suo = SmartUrbanObject('micro-' + str(i), 'microinforadiator')
325 new_suo.set_position(Position(x = 16.24 + 3.8*i, y = 16.78))
326 new_suo.set_rotation(180)
327 new_suo.is_virtual = True
328 world.add_suo(new_suo)
331 if __name__ == '__main__':
332 setup_demo_world()
333 if '--register' in sys.argv:
334 services['iot'].register()
335 services['iot'].authenticate()
336 if '--sim' in sys.argv:
337 simulator = ContextSimulator(data['context']['demo'], data['routing']['demo'])
338 simulator.set_messaging_callback(lambda suo_id, msg: services['iot'].send_info_to_device(suo_id, msg))
339 simulator.loop_async()
340 for uid in data['context']['demo'].user:
341 simulator.loop_user_intents[uid] = True
342 app.run(host = '0.0.0.0', port = 5500)