Skip to content

Commit cccc94e

Browse files
committed
Runner STM32 is working
1 parent 6d64107 commit cccc94e

File tree

6 files changed

+68
-54
lines changed

6 files changed

+68
-54
lines changed

data/openocd_stm32f4_discovery.cfg

-15
This file was deleted.

hwut_runner/hwut_runner_stm32.py

+29-10
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
class HwutRunnerStm32:
1717
def run_test(self):
1818
# Get test from server
19-
r = requests.get(self.runner_base_url + '/get', auth=self.auth)
19+
r = requests.get(self.runner_base_url + 'get', auth=self.auth)
2020
if r.status_code == 404:
21-
# Currently no job available -> try again in 10 seconds
21+
print('Currently no job available -> try again in 10 seconds')
2222
time.sleep(10)
2323
return True
2424
if r.status_code != 200:
2525
print('Error: unable to get job')
26+
print('Error: ', r.text, ', ', r.content)
2627
return False
2728
json = r.json()
2829
if not ('id' in json and
@@ -44,6 +45,7 @@ def run_test(self):
4445
r = requests.get(executable_location, auth=self.auth)
4546
if r.status_code != 200:
4647
print('Error: unable to get executable')
48+
print('Error: ', r.text, ', ', r.content)
4749
return False
4850
executable_filename = secrets.token_urlsafe(32)
4951
with open(os.path.join(FILE_STORAGE, executable_filename), 'wb') as f:
@@ -52,18 +54,23 @@ def run_test(self):
5254
print('starting job {}'.format(job_id))
5355

5456
# confirm start
55-
r = requests.post(self.runner_base_url + '/start', auth=self.auth)
57+
r = requests.post(self.runner_base_url + 'start', auth=self.auth)
5658
if r.status_code != 204:
5759
# Report error
5860
print('Error: ', r.text, ', ', r.content)
5961
return False
6062

6163
start_time = datetime.datetime.now()
6264

65+
# Avoid error if this script crashed previously
66+
if not self.serial_port.is_open:
67+
self.serial_port.close()
68+
6369
# Start serial output log capture
6470
self.serial_port.baudrate = baudrate
6571
self.serial_port.open()
66-
if not self.serial_port.is_open():
72+
print(self.serial_port)
73+
if not self.serial_port.is_open:
6774
# FIXME: abort test, report error to server
6875
print('Error: unable to open serial device')
6976
return False
@@ -72,37 +79,42 @@ def run_test(self):
7279
# Flash microcontroller
7380
# e.g. `openocd -f interface/stlink-v2-1.cfg -c "hla_serial 066BFF323637414257071840" -f target/stm32f4x.cfg \
7481
# -c "program nucleo-f411re.elf verify" -c "reset run" -c "shutdown"`
82+
7583
openocd_stdout = subprocess.check_output([
7684
'openocd',
7785
'-f',
7886
'interface/{}.cfg'.format(self.programmer_type),
7987
'-c',
80-
'"hla_serial {}'.format(self.programmer_id),
88+
'hla_serial {}'.format(self.programmer_id),
8189
'-f',
8290
'target/{}.cfg'.format(self.openocd_target),
8391
'-c',
84-
'program {} verify'.format(executable_filename),
92+
'program {} verify'.format(os.path.join(FILE_STORAGE, executable_filename)),
8593
'-c',
8694
'reset run',
8795
'-c',
8896
'shutdown',
8997
])
98+
print(openocd_stdout)
9099
# FIXME: Output? Check return value? ...
91100

92101
# Wait for timer to finish while reading serial data
93-
serial_data = ''
102+
serial_data = list()
94103
while (datetime.datetime.now() - start_time) < time_limit:
95-
serial_data += self.serial_port.read_all() # FIXME: There must be a better way...
104+
serial_data.append(self.serial_port.read_all())
105+
106+
# Debug
107+
print(b''.join(serial_data))
96108

97109
# Upload logfile
98-
r = requests.put(log_location, data=serial_data, auth=self.auth)
110+
r = requests.put(log_location, data=b''.join(serial_data), auth=self.auth)
99111
if r.status_code != 204:
100112
# Report error
101113
print('Error: ', r.text, ', ', r.content)
102114
return False
103115

104116
# mark job as finished
105-
r = requests.post(self.runner_base_url + '/stop', auth=self.auth)
117+
r = requests.post(self.runner_base_url + 'stop', auth=self.auth)
106118
if r.status_code != 204:
107119
# Report error
108120
print('Error: ', r.text, ', ', r.content)
@@ -111,6 +123,9 @@ def run_test(self):
111123
# remove executable file
112124
os.remove(os.path.join(FILE_STORAGE, executable_filename))
113125

126+
# close serial device
127+
self.serial_port.close()
128+
114129
return True
115130

116131
def __init__(self):
@@ -145,9 +160,13 @@ def main(self):
145160
while True:
146161
try:
147162
self.run_test()
163+
except KeyboardInterrupt:
164+
break
148165
except:
149166
print('Error running test')
150167
print("Unexpected error:", sys.exc_info()[0])
168+
# For debugging
169+
#time.sleep(5)
151170

152171

153172
if __name__ == "__main__":

hwut_server/api/jobs.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def submit():
1818

1919
duration_limit_seconds = 60
2020
if request.args.get('duration_limit_seconds'):
21-
duration_limit_seconds = request.args.get('duration_limit_seconds')
21+
duration_limit_seconds = int(request.args.get('duration_limit_seconds'))
2222
if duration_limit_seconds > 300:
2323
return abort(413, 'maximum execution time is 300 seconds')
2424

@@ -79,13 +79,15 @@ def get(id):
7979
@mod.route('/<int:id>', methods=['DELETE'])
8080
@requires_authentication
8181
def cancel(id):
82-
job = Jobs.query.filter(Jobs.id == id).one()
82+
job = Jobs.query.filter(Jobs.id == id).first()
83+
if job is None:
84+
return abort(404)
8385
auth = request.authorization
8486
if not auth:
8587
return abort(401)
8688
if (check_authentication(auth.username, auth.password, superuser=True)) or (
8789
check_authentication(auth.username, auth.password) and auth.username == job.owner):
88-
if job.status == JobStatus.WAITING:
90+
if job.status == JobStatus.WAITING or job.status == JobStatus.RUNNING:
8991
job.status = JobStatus.CANCELED
9092
db.session.commit()
9193
return jsonify(job.to_dict(True)), 204

hwut_server/decorators.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def check_authentication(username, password, superuser=False):
1515
:param superuser: additionally check if user is superuser
1616
"""
1717
try:
18-
user = Users.query.filter_by(name=username).one()
18+
user = Users.query.filter(Users.name == username).one()
1919
if superuser:
2020
return user.verify_password(password) and user.is_superuser()
2121
else:
@@ -54,7 +54,7 @@ def decorated_function(*args, **kwargs):
5454

5555
def check_runner_authentication(username, password):
5656
try:
57-
runner = Runners.query.filter_by(id=username).one()
57+
runner = Runners.query.filter(Runners.id == username).one()
5858
if runner.token == password and runner.enabled:
5959
return True
6060
else:

hwut_server/models/runners.py

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ def to_dict(self, extended=False):
4646
'ping_counter': self.ping_counter,
4747
'job_counter': self.job_counter,
4848
'busy': self.busy,
49+
'board': self.target_board,
50+
'microcontroller': self.target_microcontroller,
4951
}
5052
else:
5153
return {

hwut_server/runner_api/jobs.py

+30-24
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from secrets import token_urlsafe
33

4-
from flask import Blueprint, jsonify, request, abort
4+
from flask import Blueprint, jsonify, request, abort, send_file, url_for
55
from sqlalchemy import and_, or_
66
from sqlalchemy.orm.exc import NoResultFound
77

@@ -10,7 +10,7 @@
1010
from hwut_server.models import Jobs, JobStatus, Runners
1111
from hwut_server.utils import FILE_STORAGE
1212

13-
mod = Blueprint('runner_jobs', __name__)
13+
mod = Blueprint('runner_jobs', __name__, url_prefix='/runner_api')
1414

1515

1616
@mod.route('/get', methods=['GET'])
@@ -19,26 +19,23 @@ def job_get():
1919
"""
2020
Get a new job
2121
"""
22-
runner = Runners.query.filter(id=request.authorization.username).one()
22+
runner = Runners.query.filter(Runners.id == request.authorization.username).one()
2323

2424
# check if a job is already queued for the runner
25-
try:
26-
job = Jobs.query.filter(and_(Jobs.runner == runner.id, Jobs.status == JobStatus.QUEUED)).one()
25+
job = Jobs.query.filter(and_(Jobs.runner == runner.id, Jobs.status == JobStatus.QUEUED)).first()
2726
# Else queue a new job
28-
except NoResultFound:
29-
try:
30-
filters = list()
31-
filters.append(Jobs.status == JobStatus.WAITING)
32-
filters.append(Jobs.microcontroller == runner.microcontroller)
33-
filters.append(or_(Jobs.board == runner.board, Jobs.board == ''))
34-
job = Jobs.query.filter(and_(*filters)).order_by(Jobs.created.desc()).first()
35-
except NoResultFound:
27+
if job is None:
28+
#try:
29+
filters = list()
30+
filters.append(Jobs.status == JobStatus.WAITING)
31+
filters.append(Jobs.microcontroller == runner.target_microcontroller)
32+
filters.append(or_(Jobs.board == runner.target_board, Jobs.board == ''))
33+
job = Jobs.query.filter(and_(*filters)).order_by(Jobs.created.desc()).first()
34+
#except:
35+
# return abort(500)
36+
if job is None:
3637
return '', 404
37-
except:
38-
return abort(500)
39-
4038
runner.acquire()
41-
4239
job.status = JobStatus.QUEUED
4340
job.runner = runner.id
4441

@@ -47,9 +44,9 @@ def job_get():
4744

4845
return jsonify({
4946
'id': job.id,
50-
'executable_location': request.url_root + 'executable',
51-
'log_location': request.url_root + 'log',
52-
'other_location': request.url_root + 'other',
47+
'executable_location': request.url_root[:-1] + url_for('runner_jobs.job_get_executable'),
48+
'log_location': request.url_root[:-1] + url_for('runner_jobs.job_upload_log'),
49+
'other_location': request.url_root[:-1] + url_for('runner_jobs.job_upload_other'),
5350
'time_limit': job.duration_limit_seconds,
5451
'baudrate': 115200, # FIXME
5552
})
@@ -60,13 +57,15 @@ def job_get():
6057
def job_get_executable():
6158
runner_id = request.authorization.username
6259
try:
63-
job = Jobs.query.filter(and_(Jobs.runner == runner_id, Jobs.status == JobStatus.RUNNING)).one()
60+
job = Jobs.query.filter(and_(Jobs.runner == runner_id, Jobs.status == JobStatus.QUEUED)).one()
6461
except NoResultFound:
6562
return abort(400)
6663

6764
if not job.filename_executable:
6865
return abort(409, 'executable file already exists')
6966

67+
return send_file(os.path.join('..', FILE_STORAGE, job.filename_executable))
68+
7069

7170
@mod.route('/start', methods=['POST'])
7271
@requires_runner_authentication
@@ -75,8 +74,15 @@ def job_start():
7574
Runner confirms job has stopped
7675
"""
7776
runner_id = request.authorization.username
77+
78+
# mark any other job as finished
79+
jobs = Jobs.query.filter(and_(Jobs.runner == runner_id, Jobs.status == JobStatus.RUNNING)).all()
80+
for job in jobs:
81+
job.status = JobStatus.ERROR
82+
db.session.commit()
83+
7884
try:
79-
job = Jobs.query.filter(and_(Jobs.runner == runner_id, Jobs.status == JobStatus.RUNNING)).one()
85+
job = Jobs.query.filter(and_(Jobs.runner == runner_id, Jobs.status == JobStatus.QUEUED)).one()
8086
job.status = JobStatus.RUNNING
8187
db.session.commit()
8288
return '', 204
@@ -90,9 +96,9 @@ def job_stop():
9096
"""
9197
Runner confirms job is about to start
9298
"""
93-
runner = Runners.query.filter(id=request.authorization.username).one()
99+
runner = Runners.query.filter(Runners.id == request.authorization.username).one()
94100
try:
95-
job = Jobs.query.filter(and_(Jobs.runner == runner.id, Jobs.status == JobStatus.QUEUED)).one()
101+
job = Jobs.query.filter(and_(Jobs.runner == runner.id, Jobs.status == JobStatus.RUNNING)).one()
96102
if request.args.get('error') and request.args.get('error') == '1':
97103
job.status = JobStatus.ERROR
98104
else:

0 commit comments

Comments
 (0)