ULP-ActivitySupport – blob

You can use Git to clone the repository via the web URL. Download snapshot (zip)
Receive and display data from the MiR service
[ULP-ActivitySupport] / server.py
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])
227 # --- dummy profile service ---
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     },
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)
266 # Nur zum Testen, kommt irgendwann weg! :) 
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)