Skip to content

Commit 4e0e1ec

Browse files
authored
cherry pick: fix coderbot init, program load (#203)
* fix init coderbot * fix program load * bump connexion to 3.0.x * add wifi stub
1 parent c437c0f commit 4e0e1ec

File tree

12 files changed

+204
-50
lines changed

12 files changed

+204
-50
lines changed

coderbot/api.py

+1-10
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,7 @@
2727

2828
BUTTON_PIN = 16
2929

30-
config = Config.read()
31-
bot = CoderBot.get_instance(motor_trim_factor=float(config.get('move_motor_trim', 1.0)),
32-
motor_max_power=int(config.get('motor_max_power', 100)),
33-
motor_min_power=int(config.get('motor_min_power', 0)),
34-
hw_version=config.get('hardware_version'),
35-
pid_params=(float(config.get('pid_kp', 1.0)),
36-
float(config.get('pid_kd', 0.1)),
37-
float(config.get('pid_ki', 0.01)),
38-
float(config.get('pid_max_speed', 200)),
39-
float(config.get('pid_sample_time', 0.01))))
30+
bot = CoderBot.get_instance()
4031
audio_device = Audio.get_instance()
4132
cam = Camera.get_instance()
4233

coderbot/audio.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import pyaudio
2727
import alsaaudio
2828

29-
from six.moves import queue
29+
import queue
3030
# [END import_libraries]
3131

3232
# Audio recording parameters

coderbot/coderbot.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,10 @@ def exit(self):
157157
s.cancel()
158158

159159
@classmethod
160-
def get_instance(cls, motor_trim_factor=1.0, motor_max_power=100, motor_min_power=0, hw_version="5", pid_params=(0.8, 0.1, 0.01, 200, 0.01)):
160+
def get_instance(cls, motor_trim_factor=1.0, motor_max_power=100, motor_min_power=0, hw_version="5", pid_params=(0.8, 0.1, 0.01, 200, 0.01), from_defaults=True):
161161
if not cls.the_bot:
162+
if from_defaults:
163+
raise ValueError("incorrect CoderBot initialisation")
162164
cls.the_bot = CoderBot(motor_trim_factor=motor_trim_factor, motor_max_power= motor_max_power, motor_min_power=motor_min_power, hw_version=hw_version, pid_params=pid_params)
163165
return cls.the_bot
164166

@@ -272,5 +274,4 @@ def _cb_button(self, gpio, level, tick):
272274
elif tick - self._cb_last_tick[gpio] > elapse:
273275
self._cb_last_tick[gpio] = tick
274276
logging.info("pushed: %d, %d", level, tick)
275-
cb()
276-
277+
cb()

coderbot/main.py

+27-18
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import picamera
99
import connexion
1010

11-
from flask_cors import CORS
11+
from connexion.options import SwaggerUIOptions
12+
from connexion.middleware import MiddlewarePosition
13+
from starlette.middleware.cors import CORSMiddleware
1214

1315
from camera import Camera
1416
from motion import Motion
@@ -22,29 +24,27 @@
2224
# Logging configuration
2325
logger = logging.getLogger()
2426
logger.setLevel(os.environ.get("LOGLEVEL", "INFO"))
25-
# sh = logging.StreamHandler()
26-
# formatter = logging.Formatter('%(message)s')
27-
# sh.setFormatter(formatter)
28-
# logger.addHandler(sh)
2927

3028
## (Connexion) Flask app configuration
3129

3230
# Serve a custom version of the swagger ui (Jinja2 templates) based on the default one
3331
# from the folder 'swagger-ui'. Clone the 'swagger-ui' repository inside the backend folder
34-
options = {"swagger_ui": False}
35-
connexionApp = connexion.App(__name__, options=options)
36-
37-
# Connexion wraps FlaskApp, so app becomes connexionApp.app
38-
app = connexionApp.app
39-
# Access-Control-Allow-Origin
40-
CORS(app)
41-
app.debug = False
32+
swagger_ui_options = SwaggerUIOptions(swagger_ui=True)
33+
app = connexion.App(__name__, swagger_ui_options=swagger_ui_options)
34+
app.add_middleware(
35+
CORSMiddleware,
36+
position=MiddlewarePosition.BEFORE_EXCEPTION,
37+
allow_origins=["*"],
38+
allow_credentials=True,
39+
allow_methods=["*"],
40+
allow_headers=["*"],
41+
)
4242
app.prog_engine = ProgramEngine.get_instance()
4343

4444
## New API and web application
4545

4646
# API v1 is defined in v1.yml and its methods are in api.py
47-
connexionApp.add_api('v1.yml')
47+
app.add_api('v1.yml')
4848

4949
def button_pushed():
5050
if app.bot_config.get('button_func') == "startstop":
@@ -67,8 +67,16 @@ def run_server():
6767
try:
6868
try:
6969
app.bot_config = Config.read()
70-
71-
bot = CoderBot.get_instance()
70+
bot = CoderBot.get_instance(motor_trim_factor=float(app.bot_config.get('move_motor_trim', 1.0)),
71+
motor_max_power=int(app.bot_config.get('motor_max_power', 100)),
72+
motor_min_power=int(app.bot_config.get('motor_min_power', 0)),
73+
hw_version=app.bot_config.get('hardware_version'),
74+
pid_params=(float(app.bot_config.get('pid_kp', 1.0)),
75+
float(app.bot_config.get('pid_kd', 0.1)),
76+
float(app.bot_config.get('pid_ki', 0.01)),
77+
float(app.bot_config.get('pid_max_speed', 200)),
78+
float(app.bot_config.get('pid_sample_time', 0.01))),
79+
from_defaults=False)
7280

7381
try:
7482
audio_device = Audio.get_instance()
@@ -78,6 +86,7 @@ def run_server():
7886
logging.warning("Audio not present")
7987

8088
try:
89+
logging.info("starting camera")
8190
cam = Camera.get_instance()
8291
Motion.get_instance()
8392
except picamera.exc.PiCameraError:
@@ -97,12 +106,12 @@ def run_server():
97106

98107
remove_doreset_file()
99108

100-
app.run(host="0.0.0.0", port=5000, debug=False, use_reloader=False, threaded=True)
109+
app.run(host="0.0.0.0", port=5000)
101110
finally:
102111
if cam:
103112
cam.exit()
104113
if bot:
105114
bot.exit()
106115

107116
if __name__ == "__main__":
108-
run_server()
117+
run_server()

coderbot/program.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,13 @@ def __init__(self):
8787
for filename in filenames:
8888
if PROGRAM_PREFIX in filename:
8989
program_name = filename[len(PROGRAM_PREFIX):-len(PROGRAM_SUFFIX)]
90-
logging.info("adding program %s in path %s as default %r", program_name, dirname, ("default" in dirname))
91-
with open(os.path.join(dirname, filename), "r") as f:
92-
program_dict = json.load(f)
93-
program_dict["default"] = "default" in dirname
94-
program = Program.from_dict(program_dict)
95-
self.save(program)
90+
if self._programs.search(query.name == program_name) == []:
91+
logging.info("adding program %s in path %s as default %r", program_name, dirname, ("default" in dirname))
92+
with open(os.path.join(dirname, filename), "r") as f:
93+
program_dict = json.load(f)
94+
program_dict["default"] = "default" in dirname
95+
program = Program.from_dict(program_dict)
96+
self.save(program)
9697

9798
@classmethod
9899
def get_instance(cls):

coderbot/v1.yml

+43-3
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ paths:
170170
required: true
171171
schema:
172172
type: string
173+
minLength: 1
174+
maxLength: 128
175+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
176+
173177
tags:
174178
- Program management
175179
responses:
@@ -184,6 +188,9 @@ paths:
184188
required: true
185189
schema:
186190
type: string
191+
minLength: 1
192+
maxLength: 128
193+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
187194
responses:
188195
200:
189196
description: "ok"
@@ -200,6 +207,9 @@ paths:
200207
required: true
201208
schema:
202209
type: string
210+
minLength: 1
211+
maxLength: 128
212+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
203213
requestBody:
204214
description: Program object
205215
required: true
@@ -225,6 +235,9 @@ paths:
225235
required: true
226236
schema:
227237
type: string
238+
minLength: 1
239+
maxLength: 128
240+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
228241
requestBody:
229242
description: Program object
230243
required: true
@@ -248,6 +261,9 @@ paths:
248261
required: true
249262
schema:
250263
type: string
264+
minLength: 1
265+
maxLength: 128
266+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
251267
responses:
252268
200:
253269
description: "ok"
@@ -264,6 +280,9 @@ paths:
264280
required: true
265281
schema:
266282
type: string
283+
minLength: 1
284+
maxLength: 128
285+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
267286
responses:
268287
200:
269288
description: "ok"
@@ -304,6 +323,9 @@ paths:
304323
required: true
305324
schema:
306325
type: string
326+
minLength: 1
327+
maxLength: 128
328+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
307329
- name: default
308330
in: query
309331
schema:
@@ -323,6 +345,9 @@ paths:
323345
required: true
324346
schema:
325347
type: string
348+
minLength: 1
349+
maxLength: 128
350+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
326351
requestBody:
327352
description: Update Activity
328353
required: true
@@ -346,6 +371,9 @@ paths:
346371
required: true
347372
schema:
348373
type: string
374+
minLength: 1
375+
maxLength: 128
376+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
349377
responses:
350378
200:
351379
description: "ok"
@@ -386,6 +414,9 @@ paths:
386414
required: true
387415
schema:
388416
type: string
417+
minLength: 1
418+
maxLength: 128
419+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
389420
tags:
390421
- Music extensions
391422
responses:
@@ -502,12 +533,13 @@ paths:
502533
type: string
503534
minLength: 1
504535
maxLength: 256
536+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
505537
description: text to be "spoken"
506538
locale:
507539
type: string
508540
minLength: 1
509541
maxLength: 2
510-
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
542+
pattern: '^[a-zA-Z]+$'
511543
description: locale of text to be "spoken"
512544
required:
513545
- text
@@ -586,6 +618,9 @@ paths:
586618
required: true
587619
schema:
588620
type: string
621+
minLength: 1
622+
maxLength: 128
623+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
589624
tags:
590625
- CNN Models
591626
responses:
@@ -600,6 +635,9 @@ paths:
600635
required: true
601636
schema:
602637
type: string
638+
minLength: 1
639+
maxLength: 128
640+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
603641
tags:
604642
- CNN Models
605643
responses:
@@ -679,16 +717,17 @@ components:
679717
properties:
680718
name:
681719
type: string
720+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
682721
tag:
683722
type: string
684723
Program:
685724
type: object
686725
properties:
687726
name:
688727
type: string
689-
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
690728
minLength: 1
691729
maxLength: 128
730+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
692731
code:
693732
type: string
694733
minLength: 1
@@ -709,6 +748,7 @@ components:
709748
type: string
710749
minLength: 1
711750
maxLength: 128
751+
pattern: '^[a-zA-ZA-zÀ-ú0-9-_ ]+$'
712752
description:
713753
type: string
714754
minLength: 0
@@ -722,4 +762,4 @@ components:
722762
- description
723763
- default
724764
- stock
725-
765+

docker/stub/requirements.txt

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# API framework
2-
connexion==2.14.2
3-
Flask==2.2.5
4-
Flask-Cors==3.0.10
2+
connexion[uvicorn,flask,swagger-ui]==3.0.5
53
tinydb==4.8.0
6-
Werkzeug==2.2.3
74

85
# Misc utils
96
setuptools==69.2.0

docker/stub/start.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
export PYTHONPATH=./stub:./test:./coderbot
44
cd /coderbot
5-
python3 coderbot/main.py
5+
python3 coderbot/main.py & python3 stub/wifi/main.py

requirements.txt

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# API framework
2-
connexion==2.14.2
3-
Flask==2.2.5
4-
Flask-Cors==3.0.10
2+
connexion[uvicorn,flask,swagger-ui]==3.0.5
53
tinydb==4.8.0
6-
Werkzeug==2.2.3
74

85
# Misc utils
96
setuptools==69.2.0

stub/wifi/api.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import logging
2+
3+
def list_access_points():
4+
return {"ssids": [{"ssid": "my_wifi"}]}
5+
6+
def connection_status():
7+
return {"wifi": "true", "internet": "true"}
8+
9+
def connect():
10+
return "ok"
11+
12+
def forget():
13+
return "ok"
14+
15+
def sset_hotspot_ssid():
16+
return "ok"
17+
18+
def set_hotspot_password():
19+
return "ok"

0 commit comments

Comments
 (0)