forked from Archive/PX4-Autopilot
334 lines
11 KiB
Python
Executable File
334 lines
11 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
""" Script to generate Serial (UART) parameters and the ROMFS startup script """
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
try:
|
|
from jinja2 import Environment, FileSystemLoader
|
|
except ImportError as e:
|
|
print("Failed to import jinja2: " + str(e))
|
|
print("")
|
|
print("You may need to install it using:")
|
|
print(" pip3 install --user jinja2")
|
|
print("")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import yaml
|
|
except ImportError as e:
|
|
print("Failed to import yaml: " + str(e))
|
|
print("")
|
|
print("You may need to install it using:")
|
|
print(" pip3 install --user pyyaml")
|
|
print("")
|
|
sys.exit(1)
|
|
|
|
|
|
## Configuration
|
|
|
|
# All possible Serial ports
|
|
# Note: do not re-use or change indexes. When adding a port, always use an
|
|
# index that has never been used before. This is important for compatibility
|
|
# with QGC (parameter metadata)
|
|
serial_ports = {
|
|
# index 0 means disabled
|
|
# index 1000 means ethernet condiguration
|
|
|
|
# Generic
|
|
# "URT1": {
|
|
# "label": "UART 1",
|
|
# "index": 1,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
# "URT2": {
|
|
# "label": "UART 2",
|
|
# "index": 2,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
# "URT3": {
|
|
# "label": "UART 3",
|
|
# "index": 3,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
# "URT4": {
|
|
# "label": "UART 4",
|
|
# "index": 4,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
# "URT5": {
|
|
# "label": "UART 5",
|
|
# "index": 5,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
"URT6": {
|
|
"label": "UART 6",
|
|
"index": 6,
|
|
"default_baudrate": 57600,
|
|
},
|
|
# "URT7": {
|
|
# "label": "UART 7",
|
|
# "index": 7,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
# "URT8": {
|
|
# "label": "UART 8",
|
|
# "index": 8,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
# "URT9": {
|
|
# "label": "UART 9",
|
|
# "index": 9,
|
|
# "default_baudrate": 57600,
|
|
# },
|
|
|
|
# Telemetry Ports
|
|
"TEL1": { # telemetry link
|
|
"label": "TELEM 1",
|
|
"index": 101,
|
|
"default_baudrate": 57600,
|
|
},
|
|
"TEL2": { # companion port
|
|
"label": "TELEM 2",
|
|
"index": 102,
|
|
"default_baudrate": 921600,
|
|
},
|
|
"TEL3": {
|
|
"label": "TELEM 3",
|
|
"index": 103,
|
|
"default_baudrate": 57600,
|
|
},
|
|
"TEL4": {
|
|
"label": "TELEM/SERIAL 4",
|
|
"index": 104,
|
|
"default_baudrate": 57600,
|
|
},
|
|
|
|
# GPS Ports
|
|
"GPS1": {
|
|
"label": "GPS 1",
|
|
"index": 201,
|
|
"default_baudrate": 0,
|
|
},
|
|
"GPS2": {
|
|
"label": "GPS 2",
|
|
"index": 202,
|
|
"default_baudrate": 0,
|
|
},
|
|
"GPS3": {
|
|
"label": "GPS 3",
|
|
"index": 203,
|
|
"default_baudrate": 0,
|
|
},
|
|
|
|
# RC Port
|
|
"RC": {
|
|
"label": "Radio Controller",
|
|
"index": 300,
|
|
"default_baudrate": 0,
|
|
},
|
|
|
|
# WIFI Port (PixRacer)
|
|
"WIFI": {
|
|
"label": "Wifi Port",
|
|
"index": 301,
|
|
"default_baudrate": 1, # set default to an unusable value to detect that this serial port has not been configured
|
|
},
|
|
|
|
# EXT2
|
|
"EXT2": {
|
|
"label": "EXT2",
|
|
"index": 401,
|
|
"default_baudrate": 57600,
|
|
},
|
|
|
|
}
|
|
|
|
parser = argparse.ArgumentParser(description='Generate Serial params & startup script')
|
|
|
|
parser.add_argument('--serial-ports', type=str, nargs='*', metavar="TAG:DEVICE",
|
|
default=[],
|
|
help='Serial ports: mappings from the tag name to the device (e.g. GPS1:/dev/ttyS1)')
|
|
parser.add_argument('--config-files', type=str, nargs='*', default=[],
|
|
help='YAML module config file(s)')
|
|
parser.add_argument('--all-ports', action='store_true',
|
|
help='Generate output for all known ports (params file only)')
|
|
parser.add_argument('--constrained-flash', action='store_true',
|
|
help='Reduce verbosity in ROMFS scripts to reduce flash size')
|
|
parser.add_argument('--rc-dir', type=str, action='store',
|
|
help='ROMFS output directory', default=None)
|
|
parser.add_argument('--params-file', type=str, action='store',
|
|
help='Parameter output file', default=None)
|
|
parser.add_argument('--ethernet', action='store_true',
|
|
help='Ethernet support')
|
|
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
|
|
help='Verbose Output')
|
|
|
|
args = parser.parse_args()
|
|
|
|
arg_board_serial_ports = args.serial_ports
|
|
verbose = args.verbose
|
|
rc_serial_output_dir = args.rc_dir
|
|
rc_serial_template = 'rc.serial.jinja'
|
|
rc_serial_port_template = 'rc.serial_port.jinja'
|
|
serial_params_output_file = args.params_file
|
|
serial_params_template = 'serial_params.c.jinja'
|
|
generate_for_all_ports = args.all_ports
|
|
constrained_flash = args.constrained_flash
|
|
ethernet_supported = args.ethernet
|
|
|
|
if generate_for_all_ports:
|
|
board_ports = [(key, "") for key in serial_ports]
|
|
else:
|
|
# convert arg_board_serial_ports list [ "TAG:DEVICE" ] into [ ("TAG", "DEVICE") ]
|
|
board_ports = [tuple(port.split(":")) for port in arg_board_serial_ports]
|
|
|
|
|
|
if rc_serial_output_dir is None and serial_params_output_file is None:
|
|
raise Exception("At least one of --rc-dir or --params-file "
|
|
"(e.g. serial_params.c) needs to be specified")
|
|
|
|
|
|
# parse the YAML files
|
|
serial_commands = []
|
|
ethernet_configuration = []
|
|
|
|
if ethernet_supported:
|
|
ethernet_configuration.append({
|
|
'tag': "ETH",
|
|
'label': "Ethernet",
|
|
'index': 1000
|
|
})
|
|
|
|
def parse_yaml_serial_config(yaml_config):
|
|
""" parse the serial_config section from the yaml config file """
|
|
if 'serial_config' not in yaml_config:
|
|
return []
|
|
ret = []
|
|
module_name = yaml_config['module_name']
|
|
for serial_config in yaml_config['serial_config']:
|
|
if 'label' not in serial_config:
|
|
serial_config['label'] = module_name
|
|
ret.append(serial_config)
|
|
return ret
|
|
|
|
for yaml_file in args.config_files:
|
|
with open(yaml_file, 'r') as stream:
|
|
try:
|
|
yaml_config = yaml.load(stream, Loader=yaml.Loader)
|
|
serial_commands.extend(parse_yaml_serial_config(yaml_config))
|
|
|
|
except yaml.YAMLError as exc:
|
|
print(exc)
|
|
raise
|
|
|
|
|
|
# sanity check (makes sure the param names don't exceed the max length of 16 chars)
|
|
for key in serial_ports:
|
|
if len(key) > 4:
|
|
raise Exception("Serial tag {:} is too long (max length=4)".format(key))
|
|
|
|
serial_devices = []
|
|
for tag, device in board_ports:
|
|
if tag not in serial_ports:
|
|
raise Exception("Unknown serial port {:}. "
|
|
"You might have to add it to serial_ports in\n {:}".format(tag,
|
|
os.path.realpath(__file__)))
|
|
serial_devices.append({
|
|
'tag': tag,
|
|
'device': device,
|
|
'label': serial_ports[tag]["label"],
|
|
'index': serial_ports[tag]["index"],
|
|
'default_baudrate': serial_ports[tag]["default_baudrate"]
|
|
})
|
|
|
|
|
|
# construct commands based on selected board
|
|
commands = []
|
|
for serial_command in serial_commands:
|
|
num_instances = serial_command.get('num_instances', 1)
|
|
# TODO: use a loop in the script instead of explicitly enumerating all instances
|
|
for i in range(num_instances):
|
|
port_config = serial_command['port_config_param']
|
|
port_param_name = port_config['name'].replace('${i}', str(i))
|
|
|
|
# check if a port dependency is specified
|
|
if 'depends_on_port' in port_config:
|
|
depends_on_port = port_config['depends_on_port']
|
|
if not any(p['tag'] == depends_on_port for p in serial_devices):
|
|
if verbose:
|
|
print("Skipping {:} (missing dependent port)".format(port_param_name))
|
|
continue
|
|
|
|
default_port = 0 # disabled
|
|
if 'default' in port_config:
|
|
if type(port_config['default']) == list:
|
|
assert len(port_config['default']) == num_instances
|
|
default_port_str = port_config['default'][i]
|
|
else:
|
|
default_port_str = port_config['default']
|
|
|
|
if default_port_str != "":
|
|
if default_port_str not in serial_ports:
|
|
raise Exception("Default Port {:} not found for {:}".format(default_port_str, serial_command['label']))
|
|
|
|
if default_port_str in dict(board_ports).keys():
|
|
default_port = serial_ports[default_port_str]['index']
|
|
|
|
|
|
commands.append({
|
|
'command': serial_command['command'],
|
|
'label': serial_command['label'],
|
|
'instance': i,
|
|
'multi_instance': num_instances > 1,
|
|
'port_param_name': port_param_name,
|
|
'default_port': default_port,
|
|
'param_group': port_config['group'],
|
|
'description_extended': port_config.get('description_extended', ''),
|
|
'supports_networking': serial_command.get('supports_networking', False)
|
|
})
|
|
|
|
if verbose:
|
|
print("Serial Devices: {:}".format(serial_devices))
|
|
#print("Commands: {:}".format(commands))
|
|
|
|
|
|
jinja_env = Environment(loader=FileSystemLoader(
|
|
os.path.dirname(os.path.realpath(__file__))))
|
|
|
|
# generate the ROMFS script using a jinja template
|
|
if rc_serial_output_dir is not None:
|
|
if generate_for_all_ports:
|
|
raise Exception("Cannot create rc file for --all-ports")
|
|
rc_serial_output_file = os.path.join(rc_serial_output_dir, "rc.serial")
|
|
rc_serial_port_output_file = os.path.join(rc_serial_output_dir, "rc.serial_port")
|
|
|
|
if verbose: print("Generating {:}".format(rc_serial_output_file))
|
|
if len(serial_devices) == 0:
|
|
# if the board has no UARTs, create an empty rc file
|
|
open(rc_serial_output_file, 'w').close()
|
|
else:
|
|
template = jinja_env.get_template(rc_serial_template)
|
|
with open(rc_serial_output_file, 'w') as fid:
|
|
fid.write(template.render(serial_devices=serial_devices,
|
|
commands=commands,
|
|
constrained_flash=constrained_flash))
|
|
|
|
if verbose: print("Generating {:}".format(rc_serial_port_output_file))
|
|
template = jinja_env.get_template(rc_serial_port_template)
|
|
with open(rc_serial_port_output_file, 'w') as fid:
|
|
fid.write(template.render(serial_devices=serial_devices,
|
|
ethernet_configuration=ethernet_configuration,
|
|
constrained_flash=constrained_flash))
|
|
|
|
# parameter definitions
|
|
if serial_params_output_file is not None:
|
|
if verbose: print("Generating {:}".format(serial_params_output_file))
|
|
template = jinja_env.get_template(serial_params_template)
|
|
with open(serial_params_output_file, 'w') as fid:
|
|
fid.write(template.render(serial_devices=serial_devices,
|
|
ethernet_configuration=ethernet_configuration,
|
|
commands=commands, serial_ports=serial_ports))
|
|
|