Skip to content

Commit dec9171

Browse files
committed
Build and install scripts, version
1 parent ae1428c commit dec9171

File tree

5 files changed

+355
-3
lines changed

5 files changed

+355
-3
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bbctrl",
3-
"version": "2.0.1",
3+
"version": "2.0.4",
44
"homepage": "http://buildbotics.com/",
55
"repository": "https://github.com/buildbotics/bbctrl-firmware",
66
"license": "CERN-OHL-S v2",

scripts/gen-var-schema

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
#!/usr/bin/env python3
2+
3+
'''
4+
Check that the configuration variable template used on the RPi matches the
5+
variables used in the AVR.
6+
'''
7+
8+
import argparse
9+
import json
10+
import yaml
11+
from json import JSONDecoder
12+
from collections import OrderedDict
13+
14+
15+
class Schema:
16+
def load(self, path):
17+
with open(path, 'r') as f:
18+
self.process(json.load(f))
19+
20+
21+
def process(self, data): raise Exception('Not implemented')
22+
23+
24+
class ConfigSchema(Schema):
25+
def process(self, data, depth = 0, index = None):
26+
for name, entry in data.items():
27+
if 'type' in entry:
28+
if entry['type'] == 'list' and 'index' in entry:
29+
self.handle_heading(name, entry, depth)
30+
self.process(entry['template'], depth + 1, entry['index'])
31+
else: self.handle_entry(depth, OrderedDict(
32+
name = name, index = index, **entry))
33+
else:
34+
self.handle_heading(name, entry, depth)
35+
self.process(entry, depth + 1, index)
36+
37+
38+
def handle_heading(self, title, data, depth): pass
39+
def handle_entry(self, depth, entry): raise Exception('Not implemented')
40+
41+
42+
def get_type(self, entry):
43+
type = entry['type']
44+
45+
if type in ('text', 'enum'): return 'string'
46+
if type == 'float': return 'number'
47+
if type == 'int': return 'integer'
48+
if type == 'list': return 'array'
49+
if type == 'bool': return 'boolean'
50+
51+
52+
def get_units(self, entry):
53+
if 'unit' in entry:
54+
units = entry['unit']
55+
if 'iunit' in entry: units += ' or ' + entry['iunit']
56+
return units
57+
58+
59+
def get_description(self, entry):
60+
desc = []
61+
62+
if 'help' in entry: desc.append(entry['help'])
63+
if 'unit' in entry: desc.append('Units ' + self.get_units(entry) + '.')
64+
65+
if 'hmodes' in entry:
66+
desc.append('Only valid for homing modes: ' + ', '.join(entry['hmodes']))
67+
68+
return desc
69+
70+
71+
class ConfigSchemaJSONSchema(ConfigSchema):
72+
def __init__(self, path):
73+
self.schema = OrderedDict(
74+
type = 'object',
75+
description = 'Buildbotics configuration file.',
76+
required = ['version'],
77+
properties = OrderedDict(
78+
version = OrderedDict(
79+
description = 'Configuration file version number. This is the ' +
80+
'same as the version of the Buildbotics firmware which created ' +
81+
'the configuration file.',
82+
type = 'string',
83+
pattern = r'\d+\.\d+\.\d+')),
84+
patternProperties = OrderedDict())
85+
86+
self.load(path)
87+
88+
print(yaml.dump(self.schema))
89+
90+
91+
def handle_entry(self, depth, entry):
92+
name = entry['name']
93+
index = entry['index']
94+
type = self.get_type(entry)
95+
desc = self.get_description(entry)
96+
97+
p = OrderedDict(type = type)
98+
if 'default' in entry: p['default'] = entry['default']
99+
if 'values' in entry: p['enum'] = entry['values']
100+
101+
if type in ('number', 'integer'):
102+
if 'min' in entry: p['minimum'] = entry['min']
103+
if 'max' in entry: p['maximum'] = entry['max']
104+
105+
if len(desc): p['description'] = ' '.join(desc)
106+
107+
if index is None: self.schema['properties'][name] = p
108+
else: self.schema['patternProperties']['^[%s]%s$' % (index, name)] = p
109+
110+
111+
config_header = '''\
112+
# Buildbotics Controller Configuration Variables
113+
114+
Configuration variables are set on the Buildbotics controller
115+
via a JSON configuration file. The controller may be configured
116+
by uploading a configuration file via the administration panel of
117+
web interface or via the API. Individual configuration options may
118+
also set via the web interface or the API.
119+
120+
Configuration variables are organized into categories and subcategories.
121+
The variable type is noted and where appropriate, units, and minium and
122+
maximum values.
123+
124+
Some variables start with an index. An index is a single character
125+
appended to the front of the variable name and indicates an offset into
126+
an array. For example, the motor variable ``0microsteps`` is the microstep
127+
value for motor 0.
128+
129+
A formal [JSON Schema](https://json-schema.org/) specification can be
130+
found in the file [bbctrl-config-schema.yaml](bbctrl-config-schema.yaml).
131+
Note, this is in YAML format but it can be easily converted to JSON if needed.
132+
133+
'''
134+
135+
class ConfigSchemaMarkdown(ConfigSchema):
136+
def __init__(self, path):
137+
print(config_header)
138+
139+
self.load(path)
140+
141+
142+
def handle_heading(self, title, data, depth):
143+
print('#' * (2 + depth), title)
144+
if 'help' in data: print(data['help'] + '\n')
145+
146+
147+
def handle_entry(self, depth, entry):
148+
name = entry['name']
149+
index = entry.get('index')
150+
type = self.get_type(entry)
151+
units = self.get_units(entry)
152+
153+
if index is not None: name = '{index}' + name
154+
155+
print('#' * (2 + depth), name)
156+
if index is not None: print('**Index:** ``%s`` ' % index)
157+
print('**Type:** %s ' % type)
158+
159+
if units: print('**Units:** %s ' % units)
160+
161+
if 'values' in entry:
162+
print('**Enum:** ``%s`` ' %
163+
'``, ``'.join([str(v) for v in entry['values']]))
164+
165+
if 'hmodes' in entry:
166+
print('**Homing modes:** ``%s`` ' % '``, ``'.join(entry['hmodes']))
167+
168+
if type in ('number', 'integer'):
169+
if 'min' in entry: print('**Minimum:** ``%(min)s`` ' % entry)
170+
if 'max' in entry: print('**Maximum:** ``%(max)s`` ' % entry)
171+
172+
default = entry.get('default')
173+
if default:
174+
print('**Default:**', end = '')
175+
if '\n' in str(default): print('\n```\n%s\n```' % default)
176+
else: print(' ``%s`` ' % default)
177+
178+
if 'help' in entry: print('\n%(help)s' % entry)
179+
180+
print()
181+
182+
183+
184+
class VarSchema(Schema):
185+
def process(self, data):
186+
for code, entry in data.items():
187+
if code != '_': self.handle_entry(code, entry)
188+
189+
190+
def get_type(self, entry):
191+
return dict(
192+
b8 = 'integer',
193+
f32 = 'number',
194+
pstr = 'string',
195+
s32 = 'integer',
196+
str = 'string',
197+
u16 = 'integer',
198+
u32 = 'integer',
199+
u8 = 'integer'
200+
)[entry['type']]
201+
202+
203+
def get_minimum(self, entry):
204+
return dict(
205+
b8 = 0,
206+
s32 = -2147483648,
207+
u16 = 0,
208+
u32 = 0,
209+
u8 = 0
210+
).get(entry['type'])
211+
212+
213+
def get_maximum(self, entry):
214+
return dict(
215+
b8 = 1,
216+
s32 = 2147483647,
217+
u16 = 65535,
218+
u32 = 4294967295,
219+
u8 = 255
220+
).get(entry['type'])
221+
222+
223+
def handle_entry(self, code, entry): raise Exception('Not implemented')
224+
225+
226+
class VarSchemaJSONSchema(VarSchema):
227+
def __init__(self, path):
228+
self.schema = OrderedDict(
229+
type = 'object',
230+
description = 'Buildbotics internal variables schema.',
231+
properties = OrderedDict(),
232+
patternProperties = OrderedDict()
233+
)
234+
235+
self.load(path)
236+
print(yaml.dump(self.schema))
237+
238+
239+
def handle_entry(self, code, entry):
240+
type = self.get_type(entry)
241+
index = entry.get('index')
242+
desc = '%s - %s.' % (entry['name'], entry['desc'])
243+
min = self.get_minimum(entry)
244+
max = self.get_maximum(entry)
245+
246+
if entry['type'] == 'b8': desc += ' 0 for false, 1 for true.'
247+
248+
p = OrderedDict(type = type, description = desc)
249+
250+
if not entry['setable']: p['readOnly'] = True
251+
if min is not None: p['minimum'] = min
252+
if max is not None: p['maximum'] = min
253+
254+
if index is None: self.schema['properties'][code] = p
255+
else: self.schema['patternProperties']['^[%s]%s$' % (index, code)] = p
256+
257+
258+
vars_header = '''\
259+
# Buildbotics Controller Internal Variables
260+
261+
Internal variables may be read or written on the Buildbotics
262+
controller via the API. These variables are reported via the
263+
Websocket interface at ``http://bbctrl.local/api/websocket``.
264+
265+
Some variables start with an index. An index is a single
266+
character appended to the front of the variable name and
267+
indicates an offset into an array. For example, the motor
268+
variable ``0me`` is the motor enable value for motor 0.
269+
270+
These variable names are kept very short because they are used
271+
for internal communication between the Buildbotics contoller's
272+
internal RaspberryPi and AVR microcontroller.
273+
274+
A formal [JSON Schema](https://json-schema.org/) specification can be
275+
found in the file [bbctrl-vars-schema.yaml](bbctrl-vars-schema.yaml).
276+
Note, this is in YAML format but it can be easily converted to JSON if
277+
needed.
278+
279+
'''
280+
281+
282+
class VarSchemaMarkdown(VarSchema):
283+
def __init__(self, path):
284+
print(vars_header)
285+
self.load(path)
286+
287+
288+
def handle_entry(self, code, entry):
289+
name = code
290+
index = entry.get('index')
291+
type = self.get_type(entry)
292+
min = self.get_minimum(entry)
293+
max = self.get_maximum(entry)
294+
295+
if index is not None: name = '{index}' + name
296+
297+
print('##', name)
298+
print('**Full name**: %s ' % entry['name'])
299+
if index is not None: print('**Index:** ``%s`` ' % index)
300+
print('**Type:** %s ' % type)
301+
if min is not None: print('**Minimum:** %s ' % min)
302+
if max is not None: print('**Maximum:** %s ' % max)
303+
if not entry['setable']: print('**Read only** ')
304+
305+
desc = entry['desc'] + '.'
306+
if entry['type'] == 'b8': desc += ' 0 for false, 1 for true.'
307+
308+
print('\n%s' % desc)
309+
print()
310+
311+
312+
# Configure YAML output
313+
def str_rep(dump, data):
314+
style = ''
315+
if '\n' in data: style = '|'
316+
elif 50 < len(data): style = '>'
317+
318+
return dump.represent_scalar('tag:yaml.org,2002:str', data, style = style)
319+
320+
321+
def dict_rep(dump, data):
322+
value = [(dump.represent_data(k), dump.represent_data(v))
323+
for k, v in data.items()]
324+
325+
return yaml.nodes.MappingNode(u"tag:yaml.org,2002:map", value)
326+
327+
yaml.add_representer(str, str_rep)
328+
yaml.add_representer(OrderedDict, dict_rep)
329+
330+
331+
# Parse arguments
332+
parser = argparse.ArgumentParser(
333+
'Generate config and internal variable schemas and documentation.')
334+
parser.add_argument('source', choices = ('vars', 'config'),
335+
help = 'Choose the variable source.')
336+
parser.add_argument('-d', '--doc', action = 'store_true',
337+
help = 'Output Markdown documentation.')
338+
args = parser.parse_args()
339+
340+
if args.source == 'vars':
341+
path = 'src/avr/build/vars.json'
342+
if args.doc: VarSchemaMarkdown(path)
343+
else: VarSchemaJSONSchema(path)
344+
345+
if args.source == 'config':
346+
path = 'src/resources/config-template.json'
347+
if args.doc: ConfigSchemaMarkdown(path)
348+
else: ConfigSchemaJSONSchema(path)

scripts/init-devfs.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fi
1515

1616
export CBANG_HOME=$PWD/cbang
1717

18-
PROJECTS="buildbotics/updiprog buildbotics/rpipdi buildbotics/bbkbd"
18+
PROJECTS="buildbotics/updiprog buildbotics/rpipdi buildbotics/bbkbd "
1919
PROJECTS+="cauldrondevelopmentllc/cbang cauldrondevelopmentllc/camotics"
2020

2121
for PROJECT in $PROJECTS; do

scripts/install.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ PKG_DIR=$(printf "import bbctrl\nprint(bbctrl.__file__)"|python3 -)
3131
PKG_DIR=$(dirname "$(dirname "$PKG_DIR")")
3232
install bin/camotics.so $PKG_DIR/bbctrl/
3333

34+
# Synchronize files
35+
sync
36+
3437
# Restart service
3538
if $PI4; then
3639
service bbctrl start

scripts/make-image.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ KERNEL=$BUILD/kernel
1111
# Config
1212
KERNEL_DEFCONFIG=bcm2711_defconfig
1313
KERNEL_BRANCH=rpi-6.1.y
14+
BOOT_DTB=bcm2711-rpi-4-b
1415
CPUS=$(grep -c ^processor /proc/cpuinfo)
1516

1617
BOOT_FILES="bootcode.bin start.elf fixup.dat start4.elf fixup4.dat"
@@ -51,7 +52,7 @@ KERNEL_BOOT=$KERNEL/arch/$ARCH/boot
5152
mcopy -i $BOOT_IMG -s $BUILD/boot/* ::
5253
mcopy -i $BOOT_IMG -s src/overlay/boot/* ::
5354
mcopy -i $BOOT_IMG -s $KERNEL_BOOT/Image ::kernel8.img
54-
mcopy -i $BOOT_IMG -s $KERNEL_BOOT/dts/broadcom/bcm2711-rpi-4-b.dtb ::
55+
mcopy -i $BOOT_IMG -s $KERNEL_BOOT/dts/broadcom/$BOOT_DTB.dtb ::
5556
mmd -i $BOOT_IMG ::/overlays
5657
mcopy -i $BOOT_IMG -s $KERNEL_BOOT/dts/overlays/*.dtbo ::/overlays/
5758
mcopy -i $BOOT_IMG -s $KERNEL_BOOT/dts/overlays/README ::/overlays/

0 commit comments

Comments
 (0)