Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ardupilot
GitHub Repository: Ardupilot/ardupilot
Path: blob/master/Tools/autotest/param_metadata/param_parse.py
9552 views
1
#!/usr/bin/env python3
2
3
'''Generates parameter metadata files suitable for consumption by
4
ground control stations and various web services
5
6
AP_FLAKE8_CLEAN
7
8
'''
9
10
import copy
11
import os
12
import re
13
import sys
14
from argparse import ArgumentParser
15
16
from param import (Library, Parameter, Vehicle, known_group_fields,
17
known_param_fields, required_param_fields, required_library_param_fields, known_units)
18
from htmlemit import HtmlEmit
19
from rstemit import RSTEmit
20
from rstlatexpdfemit import RSTLATEXPDFEmit
21
from xmlemit import XmlEmit
22
from mdemit import MDEmit
23
from jsonemit import JSONEmit
24
25
parser = ArgumentParser(description="Parse ArduPilot parameters.")
26
parser.add_argument("-v", "--verbose", dest='verbose', action='store_true', default=False, help="show debugging output")
27
parser.add_argument("--vehicle", required=True, help="Vehicle type to generate for")
28
parser.add_argument("--no-emit",
29
dest='emit_params',
30
action='store_false',
31
default=True,
32
help="don't emit parameter documentation, just validate")
33
parser.add_argument("--legacy-params",
34
dest='emit_legacy_params',
35
action='store_true',
36
default=None,
37
help="include legacy parameters in output (default depends on format)")
38
parser.add_argument("--no-legacy-params",
39
dest='emit_legacy_params',
40
action='store_false',
41
default=None,
42
help="don't include legacy parameters in output (default depends on format)")
43
parser.add_argument("--format",
44
dest='output_format',
45
action='store',
46
default='all',
47
choices=['all', 'html', 'rst', 'rstlatexpdf', 'wiki', 'xml', 'json', 'edn', 'md'],
48
help="what output format to use")
49
50
args = parser.parse_args()
51
52
53
# Regular expressions for parsing the parameter metadata
54
55
prog_param = re.compile(r"@Param(?:{([^}]+)})?: (\w+).*((?:\n[ \t]*// @(\w+)(?:{([^}]+)})?: ?(.*))+)(?:\n[ \t\r]*\n|\n[ \t]+[A-Z]|\n\-\-\]\])", re.MULTILINE) # noqa
56
57
# match e.g @Value: 0=Unity, 1=Koala, 17=Liability
58
prog_param_fields = re.compile(r"[ \t]*// @(\w+): ?([^\r\n]*)")
59
# match e.g @Value{Copter}: 0=Volcano, 1=Peppermint
60
prog_param_tagged_fields = re.compile(r"[ \t]*// @(\w+){([^}]+)}: ([^\r\n]*)")
61
62
prog_groups = re.compile(r"@Group: *(\w*).*((?:\n[ \t]*// @(Path): (\S+))+)", re.MULTILINE)
63
64
apm_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../')
65
66
67
def find_vehicle_parameter_filepath(vehicle_name):
68
apm_tools_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../../Tools/')
69
70
vehicle_name_to_dir_name_map = {
71
"Copter": "ArduCopter",
72
"Plane": "ArduPlane",
73
"Tracker": "AntennaTracker",
74
"Sub": "ArduSub",
75
}
76
77
# first try ArduCopter/Parameters.cpp
78
for top_dir in apm_path, apm_tools_path:
79
path = os.path.join(top_dir, vehicle_name, "Parameters.cpp")
80
if os.path.exists(path):
81
return path
82
83
# then see if we can map e.g. Copter -> ArduCopter
84
if vehicle_name in vehicle_name_to_dir_name_map:
85
path = os.path.join(top_dir, vehicle_name_to_dir_name_map[vehicle_name], "Parameters.cpp")
86
if os.path.exists(path):
87
return path
88
89
raise ValueError("Unable to find parameters file for (%s)" % vehicle_name)
90
91
92
def debug(str_to_print):
93
"""Debug output if verbose is set."""
94
if args.verbose:
95
print(str_to_print)
96
97
98
def lua_applets():
99
'''return list of Library objects for lua applets and drivers'''
100
lua_lib = Library("", reference="Lua Script", not_rst=True, check_duplicates=True)
101
dirs = ["libraries/AP_Scripting/applets", "libraries/AP_Scripting/drivers"]
102
paths = []
103
for d in dirs:
104
for root, dirs, files in os.walk(os.path.join(apm_path, d)):
105
for file in files:
106
if not file.endswith(".lua"):
107
continue
108
f = os.path.join(root, file)
109
debug("Adding lua path %s" % f)
110
# the library is expected to have the path as a relative path from within
111
# a vehicle directory
112
f = f.replace(apm_path, "../")
113
paths.append(f)
114
setattr(lua_lib, "Path", ','.join(paths))
115
return lua_lib
116
117
118
libraries = []
119
120
if args.vehicle != "AP_Periph":
121
# AP_Vehicle also has parameters rooted at "", but isn't referenced
122
# from the vehicle in any way:
123
ap_vehicle_lib = Library("", reference="VEHICLE") # the "" is tacked onto the front of param name
124
setattr(ap_vehicle_lib, "Path", os.path.join('..', 'libraries', 'AP_Vehicle', 'AP_Vehicle.cpp'))
125
libraries.append(ap_vehicle_lib)
126
127
libraries.append(lua_applets())
128
129
error_count = 0
130
current_param = None
131
current_file = None
132
133
134
def error(str_to_print):
135
"""Show errors."""
136
global error_count
137
error_count += 1
138
if current_file is not None:
139
print("Error in %s" % current_file)
140
if current_param is not None:
141
print("At param %s" % current_param)
142
print(str_to_print)
143
144
145
truename_map = {
146
"Rover": "Rover",
147
"ArduSub": "Sub",
148
"ArduCopter": "Copter",
149
"ArduPlane": "Plane",
150
"AntennaTracker": "Tracker",
151
"AP_Periph": "AP_Periph",
152
"Blimp": "Blimp",
153
}
154
valid_truenames = frozenset(truename_map.values())
155
truename = truename_map.get(args.vehicle, args.vehicle)
156
157
documentation_tags_which_are_comma_separated_nv_pairs = frozenset([
158
'Values',
159
'Bitmask',
160
])
161
162
vehicle_path = find_vehicle_parameter_filepath(args.vehicle)
163
164
basename = os.path.basename(os.path.dirname(vehicle_path))
165
path = os.path.normpath(os.path.dirname(vehicle_path))
166
reference = basename # so links don't break we use ArduCopter
167
vehicle = Vehicle(truename, path, reference=reference)
168
debug('Found vehicle type %s' % vehicle.name)
169
170
171
def process_vehicle(vehicle):
172
debug("===\n\n\nProcessing %s" % vehicle.name)
173
current_file = vehicle.path+'/Parameters.cpp'
174
175
f = open(current_file)
176
p_text = f.read()
177
f.close()
178
group_matches = prog_groups.findall(p_text)
179
180
debug(group_matches)
181
for group_match in group_matches:
182
library_name = group_match[0].strip()
183
fields_text = group_match[1].strip()
184
lib = Library(library_name)
185
fields = prog_param_fields.findall(fields_text)
186
for field in fields:
187
field_name = field[0].strip()
188
field_value = field[1].strip()
189
if field_name in known_group_fields:
190
setattr(lib, field_name, field_value)
191
else:
192
error(f"group: unknown parameter metadata field '{field_name}'")
193
if not any(lib.name == parsed_l.name for parsed_l in libraries):
194
libraries.append(lib)
195
196
param_matches = []
197
param_matches = prog_param.findall(p_text)
198
199
for param_match in param_matches:
200
(only_vehicles, param_name, field_text) = (param_match[0].strip(),
201
param_match[1].strip(),
202
param_match[2].strip())
203
if len(only_vehicles):
204
only_vehicles_list = [x.strip() for x in only_vehicles.split(",")]
205
for only_vehicle in only_vehicles_list:
206
if only_vehicle not in valid_truenames:
207
raise ValueError("Invalid only_vehicle %s" % only_vehicle)
208
if vehicle.truename not in only_vehicles_list:
209
continue
210
p = Parameter(vehicle.reference+":"+param_name, current_file)
211
debug(p.name + ' ')
212
global current_param
213
current_param = p.name
214
fields = prog_param_fields.findall(field_text)
215
p.__field_text = field_text
216
field_list = []
217
for field in fields:
218
(field_name, field_value) = (field[0].strip(), field[1].strip())
219
field_list.append(field_name)
220
if field_name in known_param_fields:
221
value = re.sub('@PREFIX@', "", field_value).rstrip()
222
if hasattr(p, field_name):
223
if field_name in documentation_tags_which_are_comma_separated_nv_pairs:
224
# allow concatenation of (e.g.) bitmask fields
225
x = eval("p.%s" % field_name)
226
x += ", "
227
x += value
228
value = x
229
else:
230
error("%s already has field %s" % (p.name, field_name))
231
setattr(p, field_name, value)
232
elif field_name in frozenset(["CopyFieldsFrom", "CopyValuesFrom"]):
233
setattr(p, field_name, field_value)
234
else:
235
error(f"param: unknown parameter metadata field '{field_name}'")
236
237
if (getattr(p, 'Values', None) is not None and
238
getattr(p, 'Bitmask', None) is not None):
239
error("Both @Values and @Bitmask present")
240
241
vehicle.params.append(p)
242
current_file = None
243
debug("Processed %u params" % len(vehicle.params))
244
245
246
process_vehicle(vehicle)
247
248
debug("Found %u documented libraries" % len(libraries))
249
250
libraries = list(libraries)
251
252
alllibs = libraries[:]
253
254
255
def all_vehicles(vehicle_list: list) -> bool:
256
return len(vehicle_list) and vehicle_list[0].lower() == "all-vehicles"
257
258
259
def applicable_to_vehicle(vehicle: str, vehicle_list: list) -> bool:
260
return vehicle in vehicle_list or all_vehicles(vehicle_list)
261
262
263
def process_library(vehicle, library, pathprefix=None):
264
'''process one library'''
265
paths = library.Path.split(',')
266
for path in paths:
267
path = path.strip()
268
global current_file
269
current_file = path
270
debug("\n Processing file '%s'" % path)
271
if pathprefix is not None:
272
libraryfname = os.path.join(pathprefix, path)
273
elif path.find('/') == -1:
274
libraryfname = os.path.join(vehicle.path, path)
275
else:
276
libraryfname = os.path.normpath(os.path.join(apm_path + '/libraries/' + path))
277
if path and os.path.exists(libraryfname):
278
f = open(libraryfname)
279
p_text = f.read()
280
f.close()
281
else:
282
error("Path %s not found for library %s (fname=%s)" % (path, library.name, libraryfname))
283
continue
284
285
param_matches = prog_param.findall(p_text)
286
debug("Found %u documented parameters" % len(param_matches))
287
for param_match in param_matches:
288
(only_vehicles, param_name, field_text) = (param_match[0].strip(),
289
param_match[1].strip(),
290
param_match[2].strip())
291
if len(only_vehicles):
292
only_vehicles_list = [x.strip() for x in only_vehicles.split(",")]
293
for only_vehicle in only_vehicles_list:
294
if only_vehicle not in valid_truenames:
295
raise ValueError("Invalid only_vehicle %s" % only_vehicle)
296
if not applicable_to_vehicle(vehicle.name, only_vehicles_list):
297
continue
298
p = Parameter(library.name+param_name, current_file)
299
debug(p.name + ' ')
300
global current_param
301
current_param = p.name
302
fields = prog_param_fields.findall(field_text)
303
p.__field_text = field_text
304
field_list = []
305
for field in fields:
306
(field_name, field_value) = (field[0].strip(), field[1].strip())
307
field_list.append(field_name)
308
if field_name in known_param_fields:
309
value = re.sub('@PREFIX@', library.name, field_value)
310
if hasattr(p, field_name):
311
if field_name in documentation_tags_which_are_comma_separated_nv_pairs:
312
# allow concatenation of (e.g.) bitmask fields
313
x = eval("p.%s" % field_name)
314
x += ", "
315
x += value
316
value = x
317
else:
318
error("%s already has field %s" % (p.name, field_name))
319
setattr(p, field_name, value)
320
elif field_name in frozenset(["CopyFieldsFrom", "CopyValuesFrom"]):
321
setattr(p, field_name, field_value)
322
else:
323
error(f"param: unknown parameter metadata field '{field_name}'")
324
325
debug("matching %s" % field_text)
326
fields = prog_param_tagged_fields.findall(field_text)
327
# a parameter is considered to be vehicle-specific if
328
# there does not exist a Values: or Values{VehicleName}
329
# for that vehicle but @Values{OtherVehicle} exists.
330
seen_values_or_bitmask_for_this_vehicle = False
331
seen_values_or_bitmask_for_other_vehicle = False
332
for field in fields:
333
(field_name, only_for_vehicles, field_value) = (field[0].strip(),
334
field[1].strip(),
335
field[2].strip())
336
only_for_vehicles = only_for_vehicles.split(",")
337
only_for_vehicles = [some_vehicle.strip() for some_vehicle in only_for_vehicles]
338
if not all_vehicles(only_for_vehicles):
339
delta = set(only_for_vehicles) - set(truename_map.values())
340
if len(delta):
341
error("Unknown vehicles (%s)" % delta)
342
debug("field_name=%s vehicle=%s field[1]=%s only_for_vehicles=%s\n" %
343
(field_name, vehicle.name, field[1], str(only_for_vehicles)))
344
if field_name not in known_param_fields:
345
error(f"tagged param: unknown parameter metadata field '{field_name}'")
346
continue
347
if not applicable_to_vehicle(vehicle.name, only_for_vehicles):
348
if len(only_for_vehicles) and field_name in documentation_tags_which_are_comma_separated_nv_pairs:
349
seen_values_or_bitmask_for_other_vehicle = True
350
continue
351
352
append_value = False
353
if field_name in documentation_tags_which_are_comma_separated_nv_pairs:
354
if applicable_to_vehicle(vehicle.name, only_for_vehicles):
355
if seen_values_or_bitmask_for_this_vehicle:
356
append_value = hasattr(p, field_name)
357
seen_values_or_bitmask_for_this_vehicle = True
358
else:
359
if seen_values_or_bitmask_for_this_vehicle:
360
continue
361
append_value = hasattr(p, field_name)
362
363
value = re.sub('@PREFIX@', library.name, field_value)
364
if append_value:
365
setattr(p, field_name, getattr(p, field_name) + ',' + value)
366
else:
367
setattr(p, field_name, value)
368
369
if (getattr(p, 'Values', None) is not None and
370
getattr(p, 'Bitmask', None) is not None):
371
error("Both @Values and @Bitmask present")
372
373
if (getattr(p, 'Values', None) is None and
374
getattr(p, 'Bitmask', None) is None):
375
# values and Bitmask available for this vehicle
376
if seen_values_or_bitmask_for_other_vehicle:
377
# we've (e.g.) seen @Values{Copter} when we're
378
# processing for Rover, and haven't seen either
379
# @Values: or @Vales{Rover} - so we omit this
380
# parameter on the assumption that it is not
381
# applicable for this vehicle.
382
continue
383
384
if getattr(p, 'Vector3Parameter', None) is not None:
385
params_to_add = []
386
for axis in 'X', 'Y', 'Z':
387
new_p = copy.copy(p)
388
new_p.change_name(p.name + "_" + axis)
389
for a in ["Description"]:
390
if hasattr(new_p, a):
391
current = getattr(new_p, a)
392
setattr(new_p, a, current + " (%s-axis)" % axis)
393
params_to_add.append(new_p)
394
else:
395
params_to_add = [p]
396
397
for p in params_to_add:
398
p.path = path # Add path. Later deleted - only used for duplicates
399
if library.check_duplicates and library.has_param(p.name):
400
error("Duplicate parameter %s in %s" % (p.name, library.name))
401
continue
402
library.params.append(p)
403
404
group_matches = prog_groups.findall(p_text)
405
debug("Found %u groups" % len(group_matches))
406
debug(group_matches)
407
done_groups = dict()
408
for group_match in group_matches:
409
group = group_match[0].strip()
410
debug("Group: %s" % group)
411
do_append = True
412
if group in done_groups:
413
# this is to handle cases like the RangeFinder
414
# parameters, where the wasp stuff gets tack into the
415
# same RNGFND1_ group
416
lib = done_groups[group]
417
do_append = False
418
else:
419
lib = Library(group)
420
done_groups[group] = lib
421
422
fields = prog_param_fields.findall(group_match[1].strip())
423
for field in fields:
424
(field_name, field_value) = (field[0].strip(), field[1].strip())
425
if field_name in known_group_fields:
426
setattr(lib, field_name, field_value)
427
elif field_name in ["CopyFieldsFrom", "CopyValuesFrom"]:
428
setattr(p, field_name, field_value)
429
else:
430
error(f"unknown parameter metadata field '{field_name}'")
431
if not any(lib.Path == parsed_l.Path for parsed_l in libraries):
432
if do_append:
433
lib.set_name(library.name + lib.name)
434
debug("Group name: %s" % lib.name)
435
process_library(vehicle, lib, os.path.dirname(libraryfname))
436
if do_append:
437
alllibs.append(lib)
438
439
current_file = None
440
441
442
for library in libraries:
443
debug("===\n\n\nProcessing library %s" % library.name)
444
445
if hasattr(library, 'Path'):
446
process_library(vehicle, library)
447
else:
448
error("Skipped: no Path found")
449
450
debug("Processed %u documented parameters" % len(library.params))
451
452
453
def natural_sort_key(libname):
454
"""Natural sort key used for sorting alphanumeric strings"""
455
# splitting string into parts (numeric , non-numeric)
456
parts = re.split(r'(\d+)', libname)
457
return tuple((int(p) if p.isdigit() else p) for p in parts)
458
459
460
# sort libraries by name
461
alllibs = sorted(alllibs, key=lambda x: natural_sort_key(x.name))
462
463
libraries = alllibs
464
465
466
def is_number(numberString):
467
try:
468
float(numberString)
469
return True
470
except ValueError:
471
return False
472
473
474
def clean_param(param):
475
if (hasattr(param, "Values")):
476
valueList = param.Values.split(",")
477
new_valueList = []
478
for i in valueList:
479
(start, sep, end) = i.partition(":")
480
if sep != ":":
481
raise ValueError("Expected a colon separator in (%s)" % (i,))
482
if len(end) == 0:
483
raise ValueError("Expected a colon-separated string, got (%s)" % i)
484
end = end.strip()
485
start = start.strip()
486
new_valueList.append(":".join([start, end]))
487
param.Values = ",".join(new_valueList)
488
489
if hasattr(param, "Vector3Parameter"):
490
del param.Vector3Parameter
491
492
493
def do_copy_values(vehicle_params, libraries, param):
494
if not hasattr(param, "CopyValuesFrom"):
495
return
496
497
# so go and find the values...
498
wanted_name = param.CopyValuesFrom
499
if hasattr(param, 'Vector3Parameter'):
500
suffix = param.name[-2:]
501
wanted_name += suffix
502
503
del param.CopyValuesFrom
504
for x in vehicle_params:
505
name = x.name
506
(v, name) = name.split(":")
507
if name != wanted_name:
508
continue
509
param.Values = x.Values
510
return
511
512
for lib in libraries:
513
for x in lib.params:
514
if x.name != wanted_name:
515
continue
516
param.Values = x.Values
517
return
518
519
error("Did not find value to copy (%s wants %s)" %
520
(param.name, wanted_name))
521
522
523
def do_copy_fields(vehicle_params, libraries, param):
524
do_copy_values(vehicle_params, libraries, param)
525
526
if not hasattr(param, 'CopyFieldsFrom'):
527
return
528
529
# so go and find the values...
530
wanted_name = param.CopyFieldsFrom
531
del param.CopyFieldsFrom
532
533
if hasattr(param, 'Vector3Parameter'):
534
suffix = param.name[-2:]
535
wanted_name += suffix
536
537
for x in vehicle_params:
538
name = x.name
539
(v, name) = name.split(":")
540
if name != wanted_name:
541
continue
542
for field in dir(x):
543
if hasattr(param, field):
544
# override
545
continue
546
if field.startswith("__") or field in frozenset(["name", "real_path"]):
547
# internal methods like __ne__
548
continue
549
setattr(param, field, getattr(x, field))
550
return
551
552
for lib in libraries:
553
for x in lib.params:
554
if x.name != wanted_name:
555
continue
556
for field in dir(x):
557
if hasattr(param, field):
558
# override
559
continue
560
if field.startswith("__") or field in frozenset(["name", "real_path"]):
561
# internal methods like __ne__
562
continue
563
setattr(param, field, getattr(x, field))
564
return
565
566
error("Did not find value to copy (%s wants %s)" %
567
(param.name, wanted_name))
568
569
570
def validate(param, is_library=False):
571
"""
572
Validates the parameter meta data.
573
"""
574
global current_file
575
current_file = param.real_path
576
global current_param
577
current_param = param.name
578
# Validate values
579
if (hasattr(param, "Range")):
580
rangeValues = param.__dict__["Range"].split(" ")
581
if (len(rangeValues) != 2):
582
error("Invalid Range values for %s (%s)" %
583
(param.name, param.__dict__["Range"]))
584
return
585
min_value = rangeValues[0]
586
max_value = rangeValues[1]
587
if not is_number(min_value):
588
error("Min value not number: %s %s" % (param.name, min_value))
589
return
590
if not is_number(max_value):
591
error("Max value not number: %s %s" % (param.name, max_value))
592
return
593
# Check for duplicate in @value field
594
if (hasattr(param, "Values")):
595
valueList = param.__dict__["Values"].split(",")
596
values = []
597
for i in valueList:
598
i = i.replace(" ", "")
599
values.append(i.partition(":")[0])
600
601
# Make sure all values are numbers
602
for value in values:
603
if not is_number(value):
604
error("Value not number: \"%s\"" % value)
605
606
if (len(values) != len(set(values))):
607
error("Duplicate values found" + str({x for x in values if values.count(x) > 1}))
608
609
# Validate units
610
if (hasattr(param, "Units")):
611
if (param.__dict__["Units"] != "") and (param.__dict__["Units"] not in known_units):
612
error("unknown units field '%s'" % param.__dict__["Units"])
613
# Validate User
614
if (hasattr(param, "User")):
615
if param.User.strip() not in ["Standard", "Advanced"]:
616
error("unknown user (%s)" % param.User.strip())
617
618
# Validate description
619
if (hasattr(param, "Description")):
620
if not param.Description or not param.Description.strip():
621
error("Empty Description (%s)" % param)
622
623
# Check range and values don't contradict one another
624
if (hasattr(param, "Range") and hasattr(param, "Values")):
625
# Get the min and max of values
626
valueList = param.__dict__["Values"].split(",")
627
values = [float(v.replace(" ", "").partition(":")[0]) for v in valueList]
628
minValue = min(values)
629
maxValue = max(values)
630
631
# Get min and max range
632
rangeValues = param.__dict__["Range"].split(" ")
633
minRange = float(rangeValues[0])
634
maxRange = float(rangeValues[1])
635
636
# Check values are within range
637
if minValue < minRange:
638
error("Range of %f to %f and value of: %f" % (minRange, maxRange, minValue))
639
if maxValue > maxRange:
640
error("Range of %f to %f and value of: %f" % (minRange, maxRange, maxValue))
641
642
# Validate increment
643
if (hasattr(param, "Increment")):
644
if not is_number(param.Increment):
645
error("Increment not number: \"%s\"" % param.Increment)
646
647
required_fields = required_param_fields
648
if is_library:
649
required_fields = required_library_param_fields
650
for req_field in required_fields:
651
if not getattr(param, req_field, False):
652
error("missing parameter metadata field '%s' in %s" % (req_field, param.__field_text))
653
654
655
# handle CopyFieldsFrom and CopyValuesFrom:
656
for param in vehicle.params:
657
do_copy_fields(vehicle.params, libraries, param)
658
for library in libraries:
659
for param in library.params:
660
do_copy_fields(vehicle.params, libraries, param)
661
662
for param in vehicle.params:
663
clean_param(param)
664
665
for param in vehicle.params:
666
validate(param)
667
668
# Find duplicate names in library and fix up path
669
for library in libraries:
670
param_names_seen = set()
671
param_names_duplicate = set()
672
# Find duplicates:
673
for param in library.params:
674
if param.name in param_names_seen: # is duplicate
675
param_names_duplicate.add(param.name)
676
param_names_seen.add(param.name)
677
# Fix up path for duplicates
678
for param in library.params:
679
if param.name in param_names_duplicate:
680
param.path = param.path.rsplit('/')[-1].rsplit('.')[0]
681
else:
682
# not a duplicate, so delete attribute.
683
del param.path
684
685
for library in libraries:
686
for param in library.params:
687
clean_param(param)
688
689
for library in libraries:
690
for param in library.params:
691
validate(param, is_library=True)
692
693
if not args.emit_params:
694
sys.exit(error_count)
695
696
all_emitters = {
697
'json': JSONEmit,
698
'xml': XmlEmit,
699
'html': HtmlEmit,
700
'rst': RSTEmit,
701
'rstlatexpdf': RSTLATEXPDFEmit,
702
'md': MDEmit,
703
}
704
705
try:
706
from ednemit import EDNEmit
707
all_emitters['edn'] = EDNEmit
708
except ImportError:
709
# if the user wanted edn only then don't hide any errors
710
if args.output_format == 'edn':
711
raise
712
713
if args.verbose:
714
print("Unable to emit EDN, install edn_format and pytz if edn is desired")
715
716
# filter to just the ones we want to emit:
717
emitters_to_use = []
718
for emitter_name in all_emitters.keys():
719
if args.output_format == 'all' or args.output_format == emitter_name:
720
emitters_to_use.append(emitter_name)
721
722
# actually invoke each emitter:
723
for emitter_name in emitters_to_use:
724
emit = all_emitters[emitter_name]()
725
726
emit.emit_legacy_params = args.emit_legacy_params
727
if emit.emit_legacy_params is None:
728
if emitter_name in ('rst', 'rstlatexpdf'):
729
# do not emit legacy parameters to the Wiki
730
emit.emit_legacy_params = False
731
else:
732
emit.emit_legacy_params = True
733
734
emit.emit(vehicle)
735
736
emit.start_libraries()
737
738
# create a single parameter list for all SIM_ parameters (for rst to use)
739
sim_params = []
740
for library in libraries:
741
if library.name.startswith("SIM_"):
742
sim_params.extend(library.params)
743
sim_params = sorted(sim_params, key=lambda x : x.name)
744
745
for library in libraries:
746
if library.params:
747
# we sort the parameters in the SITL library to avoid
748
# rename, and on the assumption that an asciibetical sort
749
# gives a good layout:
750
if emitter_name == 'rst':
751
if library.not_rst:
752
continue
753
if library.name == 'SIM_':
754
library = copy.deepcopy(library)
755
library.params = sim_params
756
elif library.name.startswith('SIM_'):
757
continue
758
emit.emit(library)
759
760
emit.close()
761
762
sys.exit(error_count)
763
764