diff --git a/tools/odrive/discovery.py b/tools/odrive/discovery.py index a6536638..3137f0ae 100644 --- a/tools/odrive/discovery.py +++ b/tools/odrive/discovery.py @@ -53,7 +53,7 @@ def find_all(path, serial_number, except UnicodeDecodeError: printer("device responded on endpoint 0 with something that is not ASCII") return - printer("JSON: " + json_string) + printer("JSON: " + json_string.replace('{"name"', '\n{"name"')) printer("JSON checksum: 0x{:02X} 0x{:02X}".format(json_crc16 & 0xff, (json_crc16 >> 8) & 0xff)) try: json_data = json.loads(json_string) @@ -62,6 +62,10 @@ def find_all(path, serial_number, return json_data = {"name": "odrive", "members": json_data} obj = odrive.remote_object.RemoteObject(json_data, None, channel, printer) + + obj.__dict__['_json_data'] = json_data['members'] + obj.__dict__['_json_crc'] = json_crc16 + device_serial_number = format(obj.serial_number, 'x').upper() if hasattr(obj, 'serial_number') else "[unknown serial number]" if serial_number != None and device_serial_number != serial_number: printer("Ignoring device with serial number {}".format(device_serial_number)) diff --git a/tools/odrive/template_processor.py b/tools/odrive/template_processor.py new file mode 100644 index 00000000..757b6d6a --- /dev/null +++ b/tools/odrive/template_processor.py @@ -0,0 +1,49 @@ + +import jinja2 +import os +import json + +def get_flat_endpoint_list(json, prefix): + flat_list = [] + for item in json: + item = item.copy() + if 'type' in item: + if item['type'] in {'int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32', 'int64', 'uint64'}: + item['type'] += '_t' + is_property = True + elif item['type'] in {'bool', 'float'}: + is_property = True + else: + is_property = False + if is_property: + item['name'] = prefix + item['name'] + flat_list.append(item) + if 'members' in item: + flat_list = flat_list + get_flat_endpoint_list(item['members'], prefix + item['name'] + '.') + return flat_list + +def generate_header(odrv, template_file, output_file): + json_data = odrv._json_data + json_crc = odrv._json_crc + + endpoints = get_flat_endpoint_list(json_data, '') + + env = jinja2.Environment( + #loader = jinja2.FileSystemLoader("/Data/Projects/") + #trim_blocks=True, + #lstrip_blocks=True + ) + + # Expose helper functions to jinja template code + #env.filters["delimit"] = camel_case_to_words + + # Load and render template + template = env.from_string(template_file.read()) + output = template.render( + json_crc=json_crc, + endpoints=endpoints, + output_name=os.path.basename(output_file.name) + ) + + # Output + output_file.write(output) diff --git a/tools/odrive_header_template.h.in b/tools/odrive_header_template.h.in new file mode 100644 index 00000000..392cddd6 --- /dev/null +++ b/tools/odrive_header_template.h.in @@ -0,0 +1,34 @@ +/* +* This file was autogenerated using the "odrivetool process-template" feature. +* +* The file matches a specific firmware version. If you add/remove/rename any +* properties exposed by the ODrive, this file needs to be regenerated, otherwise +* the ODrive will ignore all commands. +*/ + +#ifndef __ODRIVE_ENDPOINTS_HPP +#define __ODRIVE_ENDPOINTS_HPP +{% macro enum_name(endpoint) %}{{ endpoint.name | replace('.', '__') | upper }}{% endmacro %} + +namespace odrive { + +static constexpr const uint16_t json_crc = 0x{{ "%0x" | format(json_crc) }}; + +enum { {% for endpoint in endpoints %} + {{enum_name(endpoint)}} = {{endpoint.id}}, +{%- endfor %} +}; + +template +struct endpoint_type; + +{% for endpoint in endpoints -%} +template<> struct endpoint_type<{{enum_name(endpoint)}}> { typedef {{endpoint.type}} type; }; +{% endfor %} + +template +using endpoint_type_t = typename endpoint_type::type; + +} + +#endif __ODRIVE_ENDPOINTS_HPP diff --git a/tools/odrivetool b/tools/odrivetool index 901ebde3..eba8e829 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -6,6 +6,7 @@ ODrive command line utility from __future__ import print_function import sys import argparse +import os import odrive.discovery from odrive.utils import Logger, Event @@ -19,6 +20,7 @@ def print(*args, **kwargs): file = kwargs.get('file', sys.stdout) file.flush() if file is not None else sys.stdout.flush() +script_path=os.path.dirname(os.path.realpath(__file__)) ## Parse arguments ## parser = argparse.ArgumentParser(description='ODrive command line utility\n' @@ -36,6 +38,13 @@ shell_parser.add_argument("--no-ipython", action="store_true", dfu_parser = subparsers.add_parser('dfu', help="Upgrade the ODrive device firmware") dfu_parser.add_argument('file', metavar='HEX', help='The .hex file to be flashed. Make sure your firmware board version matches the actual board version.') +template_processor_parser = subparsers.add_parser('process-template', help="Process a jinja2 template, passing the ODrive's JSON data as data input") +template_processor_parser.add_argument("-t", "--template", type=argparse.FileType('r'), + help="the code template") +template_processor_parser.add_argument("-o", "--output", type=argparse.FileType('w'), default='-', + help="path of the generated output") +template_processor_parser.set_defaults(template = os.path.join(script_path, 'odrive_header_template.h.in')) + subparsers.add_parser('liveplotter', help="Upgrade the ODrive's Firmware") subparsers.add_parser('drv-status', help="Show status of the on-board DRV8301 chips (for debugging only)") subparsers.add_parser('rate-test', help="Estimate the average transmission bandwidth over USB") @@ -138,6 +147,11 @@ try: elif args.command == 'udev-setup': from odrive.utils import setup_udev_rules setup_udev_rules(logger) + + elif args.command == 'process-template': + from odrive.template_processor import generate_header + my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number) + generate_header(my_odrive, args.template, args.output) else: raise Exception("unknown command: " + args.command)