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 state = services['iot'].get_device_state(suo_id)
96 result['transient']['suo_state'][suo_id] = state
97 return jsonify(result)
99 @app.route('/routing/<id>')
100 def get_routing_network(id):
101 routing_engine = data['routing'].get(id)
102 if routing_engine is None:
103 return 'Invalid routing context ID', 401
104 result = copy.deepcopy(routing_engine.get_network())
105 return jsonify(result)
107 @app.route('/routing/<id>/route_nodes/<nodeA>/<nodeB>')
108 def get_route_nodes(id, nodeA, nodeB):
109 routing_engine = data['routing'].get(id)
110 if routing_engine is None:
111 return 'Invalid routing context ID', 401
112 result = routing_engine.route_between_nodes(nodeA, nodeB)
113 return jsonify(result)
115 @app.route('/routing/<id>/route/<coordsA>/<coordsB>')
116 def get_route_coords(id, coordsA, coordsB):
117 routing_engine = data['routing'].get(id)
118 if routing_engine is None:
119 return 'Invalid routing context ID', 401
120 coordsA = coordsA.split(',')
121 coordsA = { 'x': coordsA[0], 'y': coordsA[1] }
122 coordsB = coordsB.split(',')
123 coordsB = { 'x': coordsB[0], 'y': coordsB[1] }
124 result = routing_engine.route_between_coords(coordsA, coordsB)
125 return jsonify(result)
127 @app.route('/routing/<id>/route/<uids>')
128 def get_route_for_user(id, uids):
129 routing_engine = data['routing'].get(id)
130 if routing_engine is None:
131 return 'Invalid routing context ID', 401
132 result = {}
133 for uid in uids.split(','):
134 result[uid] = routing_engine.get_user_route(uid)
135 return jsonify(result)
137 @app.route('/<cid>/user/<uid>/update_position')
138 def update_position(cid, uid):
139 context = data['context'].get(cid)
140 if context is None:
141 return 'Invalid context ID', 401
142 user = context.user.get(uid)
143 if user is None:
144 return 'Invalid user ID', 401
145 lat = request.args.get('lat')
146 lon = request.args.get('lon')
147 if lat is None or lon is None:
148 return 'Must provide lat and lon parameters', 401
149 user.set_position(Position(float(lat), float(lon)))
150 return jsonify(user)
152 @app.route('/<cid>/admin/<command>/<param>')
153 def admin_command(cid, command, param):
154 context = data['context'].get(cid)
155 if context is None:
156 return 'Invalid context ID', 401
157 if command not in ['enable_direct_control', 'disable_direct_control', 'enable_pause', 'disable_pause', 'set_goal', 'set_mobility']:
158 return 'Invalid command', 401
159 else:
160 if ';' in param:
161 user = context.user.get(param.split(';')[0])
162 else:
163 user = context.user.get(param)
164 if user is None:
165 return 'Invalid user ID', 401
166 if not user.is_virtual:
167 return 'User is not virtual - cannot be controlled', 401
168 if command == 'enable_direct_control':
169 user = context.user[param]
170 if hasattr(user, 'original_intent'):
171 return 'User already under direct control', 401
172 user.original_intent = user.intent
173 user.intent = {}
174 if command == 'disable_direct_control':
175 user = context.user[param]
176 if not hasattr(user, 'original_intent'):
177 return 'User was not under direct control', 401
178 user.intent = user.original_intent
179 del user.original_intent
180 if command == 'enable_pause':
181 user = context.user[param]
182 if hasattr(user, 'paused') and user.paused:
183 return 'User already paused', 401
184 user.paused = True
185 if command == 'disable_pause':
186 user = context.user[param]
187 if not hasattr(user, 'paused') or not user.paused:
188 return 'User was not paused', 401
189 del user.paused
190 if command == 'set_goal':
191 uid, goal = param.split(';')
192 user = context.user[uid]
193 if not hasattr(user, 'original_intent'):
194 return 'User is not under direct control', 401
195 x, y = goal.split(',');
196 user.intent = { 1: Intent(1, Position( x = float(x), y = float(y))) }
197 if command == 'set_mobility':
198 uid, mobility = param.split(';')
199 user = context.user[uid]
200 if mobility not in ['foot', 'walker', 'wheelchair', 'scooter']:
201 return 'Invalid mobility', 401
202 user.mobility = mobility
203 return jsonify('success')
205 @app.route('/update_suo_list')
206 def update_suo_list(context = 'demo'):
207 devices = services['iot'].get_devices()
208 if devices is None: # connection error or something else we can't fix
209 return jsonify(None)
210 for device in devices:
211 if isinstance(services['iot'], MicroInfoRadiatorService):
212 new_suo = SmartUrbanObject(device['_id']['$oid'], 'microinforadiator')
213 del device['_id']
214 lat, lon = device['location']['coordinates']
215 del device['location']
216 else:
217 new_suo = SmartUrbanObject(device['id'], device['type'])
218 lat, lon = 0, 0 # TODO properly set position
219 new_suo.set_position(Position(lat = float(lat), lon = float(lon)))
220 for attrib in device:
221 setattr(new_suo, attrib, device[attrib])
222 data['context'][context].add_suo(new_suo)
223 return jsonify(data['context'][context])
226 #
227 # --- dummy profile service ---
228 #
230 users = {
231 '17318505-bdf4-4cb0-b918-5e073de20979': {
232 'first_name': 'Margot',
233 'last_name': 'Nowak',
234 'birth_year': 1938,
235 'bluetooth_ids': ['d6:ee:d4:16:ed:fc'],
236 'color_preference': [255, 0, 0],
237 },
238 '29673db0-35f6-45af-900d-edc15e65c831': {
239 'first_name': 'Emrah',
240 'last_name': 'Dogan',
241 'birth_year': 1927,
242 'bluetooth_ids': ['f6:be:90:32:3c:5e', 'eb:e3:0f:49:84:d2'],
243 'color_preference': [0, 0, 255],
244 },
245 }
247 @app.route('/users')
248 def get_users():
249 return jsonify(users)
251 @app.route('/user/<id>')
252 def get_user(id):
253 return jsonify(users.get(id))
255 @app.route('/user/by-bluetooth/<btid>')
256 def get_user_by_bluetooth(btid):
257 btid = btid.lower()
258 result = None
259 for user in users:
260 if btid in users[user]['bluetooth_ids']:
261 result = users[user]
262 return jsonify(result)
265 #
266 # Nur zum Testen, kommt irgendwann weg! :)
267 #
268 def setup_demo_world():
269 world = Context('demo', 'Scooter-Park')
270 world.set_boundaries(54.135, 54.135)
271 routing_engine = RoutingEngine(world)
272 i = 0
273 for user_id in users:
274 user = User(user_id, users[user_id]['first_name'] + ' ' + users[user_id]['last_name'])
275 user.color_preference = users[user_id]['color_preference']
276 user.mobility = 'foot'
277 user.is_virtual = True
278 if i == 0:
279 pos = Position(x = 6.0, y = 41.0)
280 rotation = 0
281 user.add_intent(Intent(1, Position(x = 34.2, y = 44.6)))
282 user.add_intent(Intent(2, Position(x = 41.3, y = 11.2)))
283 user.add_intent(Intent(3, Position(x = 22.6, y = 11.2)))
284 user.add_intent(Intent(4, Position(x = 6.3, y = 18.8)))
285 user.add_intent(Intent(5, Position(x = 6.0, y = 41.0)))
286 else:
287 pos = Position(x = 22.1, y = 20.4)
288 rotation = 270
289 user.add_intent(Intent(1, Position(x = 38.0, y = 33.0)))
290 user.add_intent(Intent(2, Position(x = 13.8, y = 29.7)))
291 user.add_intent(Intent(3, Position(x = 22.1, y = 20.4)))
292 user.set_position(pos)
293 user.set_rotation(rotation)
294 world.add_user(user)
295 i += 1
296 data['context']['demo'] = world
297 data['routing']['demo'] = routing_engine
298 for i in range(3):
299 new_suo = SmartUrbanObject('micro-' + str(i), 'microinforadiator')
300 new_suo.set_position(Position(x = 16.24 + 3.8*i, y = 16.78))
301 new_suo.set_rotation(180)
302 new_suo.is_virtual = True
303 world.add_suo(new_suo)
306 if __name__ == '__main__':
307 setup_demo_world()
308 if '--register' in sys.argv:
309 services['iot'].register()
310 services['iot'].authenticate()
311 if '--sim' in sys.argv:
312 simulator = ContextSimulator(data['context']['demo'], data['routing']['demo'])
313 simulator.set_messaging_callback(lambda suo_id, msg: services['iot'].send_info_to_device(suo_id, msg))
314 simulator.loop_async()
315 for uid in data['context']['demo'].user:
316 simulator.loop_user_intents[uid] = True
317 app.run(host = '0.0.0.0', port = 5500)