diff options
-rwxr-xr-x | monkey/action.py | 2 | ||||
-rwxr-xr-x | robot/main.py | 127 | ||||
-rw-r--r-- | server/user_session.py | 16 |
3 files changed, 136 insertions, 9 deletions
diff --git a/monkey/action.py b/monkey/action.py index bb046da..e894dfc 100755 --- a/monkey/action.py +++ b/monkey/action.py @@ -51,7 +51,7 @@ _packet_action_map = { 'ins': lambda packet, time, code: Action('insert', time, offset=packet['off'], text=packet['txt']), 'rm': lambda packet, time, code: Action('remove', time, offset=packet['off'], text=packet['txt']), 'tst': lambda packet, time, code: Action('test', time, total=packet['tot'], passed=packet['pas']), - 'hnt': lambda packet, time, code: Action('hint', time) + 'hnt': lambda packet, time, code: Action('hint', time), 'slva': lambda packet, time, code: Action('solve_all', time, text=packet['qry']), 'prolog_query': lambda packet, time, code: Action('prolog_query', time, text=packet['qry']), 'prolog_next': lambda packet, time, code: Action('next', time), diff --git a/robot/main.py b/robot/main.py new file mode 100755 index 0000000..06f10a0 --- /dev/null +++ b/robot/main.py @@ -0,0 +1,127 @@ +#!/usr/bin/python3 + +# This file must be run on the EV3 brick. It serves websocket connections, +# sending sensor updates and running user programs. + +PORT = 8000 + +import json +import multiprocessing +import os +import os.path +import signal + +import engineio +import eventlet + +import ev3dev + +ev3_names = { + 'lego-ev3-gyro': 'gyroscope', + 'lego-ev3-touch': 'touch', + 'lego-ev3-us': 'ultrasonic' +} + +ev3_motors = [ + ev3dev.motor(ev3dev.OUTPUT_A), + ev3dev.motor(ev3dev.OUTPUT_B), + ev3dev.motor(ev3dev.OUTPUT_C), + ev3dev.motor(ev3dev.OUTPUT_D) +] + +connections = set() +process = None + +def _run_exec(program): + import ev3dev + from mindstorms_widgets import mindstorms_widgets + exec(program) + +def run(program): + global process + stop() + process = multiprocessing.Process(target=_run_exec, args=[program]) + process.start() + +def stop(): + global process + if process is not None: + try: + os.kill(process.pid, signal.SIGKILL) + except: + pass + process = None + + for motor in ev3_motors: + if motor.connected: + motor.stop(stop_command='brake') + +def notifier(): + def read_sensor(path): + with open(os.path.join(path, 'driver_name'), 'r') as f: + name = f.read().strip() + with open(os.path.join(path, 'value0'), 'r') as f: + value = int(f.read().strip()) + with open(os.path.join(path, 'decimals'), 'r') as f: + decimals = int(f.read().strip()) + if decimals > 0: + value /= 10**decimals + with open(os.path.join(path, 'units'), 'r') as f: + units = f.read().strip() + if units: + value = '{} {}'.format(value, units) + return (ev3_names.get(name, name), value) + + sensors_path = '/sys/class/lego-sensor' + while True: + eventlet.sleep(0.2) + try: + message = {'event': 'update', 'sensors': {}} + for sensor in os.listdir(sensors_path): + name, value = read_sensor(os.path.join(sensors_path, sensor)) + message['sensors'][name] = value + + text = json.dumps(message) + for sid in connections: + eio.send(sid, text) + except: + pass + +eio = engineio.Server(async_mode='eventlet', cookie=None) +app = engineio.Middleware(eio) + +@eio.on('connect') +def connect(sid, environ): + print('New connection: {}'.format(sid)) + connections.add(sid) + +@eio.on('disconnect') +def disconnect(sid): + print('Dropped connection: {}'.format(sid)) + try: + connections.remove(sid) + except: + pass + +@eio.on('message') +def message(sid, msg): + reply = None + action = msg.get('action') + + if action is None: + reply = {'code': 1, 'message': 'Request does not contain an action'} + elif action == 'run': + program = msg.get('program') + if program is None: + reply = {'code': 2, 'message': 'Program not specified'} + else: + run(program) + reply = {'code': 0} + elif action == 'stop': + stop() + reply = {'code': 0} + + eio.send(sid, reply) + +eventlet.spawn_n(notifier) +eventlet.wsgi.server(eventlet.listen(('', PORT)), app) diff --git a/server/user_session.py b/server/user_session.py index 9338c2c..41c77ac 100644 --- a/server/user_session.py +++ b/server/user_session.py @@ -66,7 +66,7 @@ class UserSession(object): try: cur = conn.cursor() try: - cur.execute('select id, password, name, email, date_joined, gui_lang from codeq_user where username = %s', (username,)) + cur.execute('select id, password, name, email, date_joined, gui_lang, robot_address from codeq_user where username = %s', (username,)) row = cur.fetchone() if row is None: raise AuthenticationFailed('No such user: {}'.format(username)) @@ -74,7 +74,7 @@ class UserSession(object): cur.execute('update codeq_user set last_login = %s where id = %s', (str(now), row[0],)) self.uid = row[0] self.username = username - self.settings = {'gui_lang': row[5]} + self.settings = {'gui_lang': row[5], 'robot_address': row[6]} return row[2], row[3], row[4], now else: raise AuthenticationFailed('Password mismatch') @@ -142,7 +142,7 @@ class UserSession(object): try: cur = conn.cursor() try: - cur.execute("update codeq_user set gui_lang = %s where id = %s", (self.settings['gui_lang'], self.uid)) + cur.execute("update codeq_user set gui_lang = %s, robot_address = %s where id = %s", (self.settings['gui_lang'], self.settings['robot_address'], self.uid)) finally: cur.close() finally: @@ -266,9 +266,9 @@ class UserSession(object): cur.execute("""select * from( (SELECT distinct - l.name as language, + l.identifier as language, pg.id as problem_group_id, - pg.name as problem_group, + pg.identifier as problem_group, count(distinct p.id) as all, count(CASE s.done WHEN true THEN true END) as done, count(CASE s.done WHEN false THEN true END) as in_progress @@ -276,11 +276,11 @@ class UserSession(object): INNER JOIN language l ON p.language_id = l.id INNER JOIN problem_group pg ON p.problem_group_id = pg.id LEFT JOIN solution s ON s.problem_id = p.id AND s.codeq_user_id = %s - GROUP BY l.name, pg.id, pg.name) + GROUP BY l.identifier, pg.id, pg.identifier) UNION ALL (SELECT distinct - l.name as language, + l.identifier as language, 0 as problem_group_id, null as problem_group, count(distinct p.id) as all, @@ -289,7 +289,7 @@ class UserSession(object): FROM problem p INNER JOIN language l ON p.language_id = l.id LEFT JOIN solution s ON s.problem_id = p.id AND s.codeq_user_id = %s - GROUP BY l.name) + GROUP BY l.identifier) ) as u ORDER BY u.language, problem_group_id""", (uid, uid,)) #return cur.fetchall() |