Skip to content

Commit cb29665

Browse files
authored
prepare 4.1 (#161)
* tf 2.7 * revert tf 2.1 * bump numpy ver * bump requirements_stub * bump requirements_stub * led ws2812b * led ws2812b * led ws2812b * add listMusicPackages api * fix musicPackage export * remove coderbot-copy.py * fix music package removal * v1 feature parity * fix #155 * fix program status and log with v2 * update ci * add default activity init * add default activity init * add default activity * activity default parameters * bullseye wip * bullseye wip * Update audioControls.py bullseye: "PCM" => "Headphone" * bullseye wip * Update audio.py fix audio sampling rate * update stock activities * Update program.py fix Program.load() when program non found * Update activity_default.json add "shadow" input blocks * Update coderbot.py * no longer init_activities * move init_activities on frontend for i18n
1 parent 4366c40 commit cb29665

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+351
-639
lines changed

.circleci/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
build:
88
docker:
99
# specify the version you desire here
10-
- image: coderbot/python-gpac:3.7
10+
- image: coderbot/coderbot-ci:3.9-bullseye-ffmpeg
1111

1212
working_directory: ~/repo
1313

activity.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from tinydb import TinyDB, Query
2+
from tinydb.operations import delete
3+
import json
4+
5+
# Programs and Activities databases
6+
class Activities():
7+
_instance = None
8+
9+
@classmethod
10+
def get_instance(cls):
11+
if cls._instance == None:
12+
cls._instance = Activities()
13+
return cls._instance
14+
15+
def __init__(self):
16+
self.activities = TinyDB("data/activities.json")
17+
self.query = Query()
18+
19+
def load(self, name, default):
20+
if name:
21+
return self.activities.search(self.query.name == name)[0]
22+
elif default is not None:
23+
default_Activities = self.activities.search(self.query.default == True)
24+
if len(self.activities.search(self.query.default == True)) > 0:
25+
return self.activities.search(self.query.default == True)[0]
26+
else:
27+
return None
28+
29+
def save(self, activity):
30+
if self.activities.search(self.query.name == activity["name"]) == []:
31+
self.activities.insert(activity)
32+
else:
33+
if activity.get("default", False) == True:
34+
self.activities.update({'default': False})
35+
self.activities.update(activity, self.query.name == activity["name"])
36+
37+
def delete(self, activity):
38+
activity = self.activities.search(self.query.name == activity["name"])[0]
39+
if activity.get("default", False) == True:
40+
self.activities.update({'default': True}, self.query.stock == True)
41+
self.activities.remove(self.query.name == activity["name"])
42+
43+
def list(self):
44+
return self.activities.all()
45+

api.py

+41-24
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
import os
77
import subprocess
88
import json
9+
import logging
910
import connexion
10-
from tinydb import TinyDB, Query
11-
from tinydb.operations import delete
11+
import pigpio
1212
from cachetools import cached, TTLCache
1313
from coderbot import CoderBot
1414
from program import ProgramEngine, Program
1515
from config import Config
16+
from activity import Activities
1617
from coderbotTestUnit import run_test as runCoderbotTestUnit
17-
import pigpio
18+
from cnn_manager import CNNManager
1819
from musicPackages import MusicPackageManager
1920

2021
BUTTON_PIN = 16
@@ -25,8 +26,6 @@
2526
encoder=bool(bot_config.get("encoder"))
2627
)
2728

28-
query = Query()
29-
3029
def get_serial():
3130
"""
3231
Extract serial from cpuinfo file
@@ -108,8 +107,7 @@ def get_info():
108107
prog = None
109108
prog_engine = ProgramEngine.get_instance()
110109

111-
# Programs and Activities databases
112-
activities = TinyDB("data/activities.json")
110+
activities = Activities.get_instance()
113111

114112
## Robot control
115113

@@ -133,7 +131,8 @@ def turn(data):
133131

134132
def exec(data):
135133
program = prog_engine.create(data["name"], data["code"])
136-
return json.dumps(program.execute())
134+
options = data["options"]
135+
return json.dumps(program.execute(options))
137136

138137
## System
139138

@@ -173,14 +172,23 @@ def restoreSettings():
173172
Config.get()
174173
return "ok"
175174

175+
176176
def updateFromPackage():
177177
os.system('sudo bash /home/pi/clean-update.sh')
178178
file_to_upload = connexion.request.files['file_to_upload']
179179
file_to_upload.save(os.path.join('/home/pi/', 'update.tar'))
180180
os.system('sudo reboot')
181181
return 200
182182

183-
def updatePackages():
183+
def listMusicPackages():
184+
"""
185+
list available music packages
186+
"""
187+
musicPkg = MusicPackageManager.get_instance()
188+
response = musicPkg.listPackages()
189+
return json.dumps(response)
190+
191+
def updateMusicPackages():
184192
"""
185193
Add a musical package an save the list of available packages on disk
186194
also add sounds and directory
@@ -196,14 +204,23 @@ def updatePackages():
196204
if response == 1:
197205
return 200
198206
elif response == 2:
199-
return 2
207+
return 400
200208
elif response == 3:
201-
return 3
209+
return 400
202210

211+
def deleteMusicPackage(package_data):
212+
"""
213+
Delete a musical package an save the list of available packages on disk
214+
also delete package sounds and directory
215+
"""
216+
musicPkg = MusicPackageManager.get_instance()
217+
musicPkg.deletePackage(package_data['package_name'])
218+
return 200
203219

204220
## Programs
205221

206-
def saveProgram(data, overwrite):
222+
def saveProgram(data):
223+
overwrite = data["overwrite"]
207224
existing_program = prog_engine.load(data["name"])
208225
if existing_program and not overwrite:
209226
return "askOverwrite"
@@ -227,23 +244,17 @@ def listPrograms():
227244
## Activities
228245

229246
def saveActivity(data):
230-
data = data["activity"]
231-
if activities.search(query.name == data["name"]) == []:
232-
activities.insert(data)
233-
return 200
234-
else:
235-
activities.update(data, query.name == data["name"])
236-
return 200
247+
activity = data["activity"]
248+
activities.save(activity)
237249

238-
def loadActivity(name):
239-
return activities.search(query.name == name)[0], 200
250+
def loadActivity(name=None, default=None):
251+
return activities.load(name, default)
240252

241253
def deleteActivity(data):
242-
activities.remove(query.name == data["name"])
243-
254+
activities.delete(data), 200
244255

245256
def listActivities():
246-
return activities.all()
257+
return activities.list()
247258

248259
def resetDefaultPrograms():
249260
"""
@@ -273,3 +284,9 @@ def testCoderbot(data):
273284
# taking first JSON key value (varargin)
274285
tests_state = runCoderbotTestUnit(data[list(data.keys())[0]])
275286
return tests_state
287+
288+
def list_cnn_models():
289+
cnn = CNNManager.get_instance()
290+
logging.info("cnn_models_list")
291+
return json.dumps(cnn.get_models())
292+

atmega328p.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
CMD_RESET = 0x00
1717
CMD_SET_DATA = 0x01
1818
CMD_GET_DATA = 0x02
19+
CMD_SET_MODE = 0x03
20+
CMD_SET_LED = 0x04
1921

2022
ADDR_AI_FIRST = 0x00
2123
ADDR_AI_LAST = 0x01
@@ -35,7 +37,7 @@ def get_instance(cls):
3537
return cls._instance
3638

3739
def __init__(self):
38-
#Initialze the SPI
40+
# Initialze the SPI
3941
self.spi = spidev.SpiDev()
4042
self.spi.open(0,0)
4143
self.spi.max_speed_hz = BAUDRATE_MAX
@@ -54,6 +56,23 @@ def analogRead(self, addr):
5456
resp = self.spi.xfer([START, CMD_GET_DATA, addr, 0, 0], BAUDRATE)
5557
return resp[3]
5658

59+
def setLed(self, begin_led, end_led, red, green, blue):
60+
resp = self.spi.xfer([START, CMD_SET_LED,
61+
min(max(begin_led, 0), 60),
62+
min(max(end_led, 0), 60),
63+
min(max(red, 0), 254),
64+
min(max(green, 0), 254),
65+
min(max(blue, 0), 254)], BAUDRATE)
66+
return resp[3]
67+
68+
def set_led(self, begin_led, end_led, red, green, blue):
69+
begin = begin_led - 1
70+
end = end_led - 1
71+
red = int(red * 255 / 100)
72+
green = int(green * 255 / 100)
73+
blue = int(blue * 255 / 100)
74+
return self.setLed(begin, end, red, green, blue)
75+
5776
def get_input(self, addr):
5877
if addr >= ADDR_AI_FIRST and addr <= ADDR_AI_LAST:
5978
return self.analogRead(addr)

audio.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
# [END import_libraries]
3636

3737
# Audio recording parameters
38-
RATE = 16000
38+
RATE = 44100
3939
CHUNK = int(RATE / 10) # 100ms
4040
FORMAT = pyaudio.paInt16
4141

audioControls.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ def getVolume(self):
4747
def setVolume(self,valueVolume):
4848
self.mixer.setvolume(valueVolume)
4949

50-
if __name__ == "__main__":
51-
a = AudioCtrl()
52-
a.setVolume(20)
53-
#a.setVolume(100)
50+
# if __name__ == "__main__":
51+
# a = AudioCtrl()
52+
# a.setVolume(20)

cnn_classifier.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,23 @@
2323
import logging
2424

2525
import numpy as np
26-
from tensorflow.lite.python.interpreter import Interpreter
26+
try:
27+
from tensorflow.lite.python.interpreter import Interpreter
28+
except:
29+
logging.warning("tensorflow not available (for inference)")
30+
try:
31+
from tflite_runtime.interpreter import Interpreter
32+
except:
33+
logging.warning("tflite not available")
34+
2735
import cv2
2836

2937
logger = logging.getLogger(__name__)
3038

3139
class CNNClassifier(object):
3240
def __init__(self, model_file, label_file):
3341
logger.info(model_file)
34-
self._interpreter = Interpreter(model_path=model_file)
35-
self._interpreter.set_num_threads(4)
42+
self._interpreter = Interpreter(model_path=model_file, num_threads=4)
3643
self._interpreter.allocate_tensors()
3744
self._labels = self.load_labels(label_file)
3845
self._input_details = self._interpreter.get_input_details()

cnn_manager.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
import json
2727
import threading
2828

29-
from cnn_train import CNNTrainer
29+
try:
30+
from cnn_train import CNNTrainer
31+
except:
32+
logging.warning("tensorflow not available (for training)")
33+
3034
from cnn_classifier import CNNClassifier
3135

3236
MODEL_PATH = "./cnn_models"
@@ -117,6 +121,7 @@ def load_model(self, model_name):
117121
return CNNClassifier(model_file=MODEL_PATH + "/" + model_name + ".tflite",
118122
label_file=MODEL_PATH + "/" + model_name + ".txt")
119123
return None
124+
120125
class TrainThread(threading.Thread):
121126

122127
def __init__(self, manager, model_name, architecture, image_tags, photos_metadata, training_steps, learning_rate):

0 commit comments

Comments
 (0)