summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmonkey/action.py2
-rwxr-xr-xrobot/main.py127
-rw-r--r--server/user_session.py16
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()