Skip to content

Commit

Permalink
andrewi/293 cangen refactor (#315)
Browse files Browse the repository at this point in the history
* changed jninja generation function

* changes jninja functions

* inline jninja generation

* changed function names cangen

* moved jninja generation into its own function

* added -v flag and changed logging levels

* ruff formated

* refactored jninja environment setup

* formatted ruff

* changed generate code

* changed output_dir function

* fixed typo
  • Loading branch information
AndrewI26 authored Nov 12, 2024
1 parent fe08ab9 commit ca8ae9d
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 54 deletions.
1 change: 0 additions & 1 deletion docs/includes/gen_glossary.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

glossary: list[dict] = []
with open(HERE / file_glossary_input, "r") as f:

for idx, line in enumerate(f.readlines()):
match = re.match(term_pattern, line)
if match:
Expand Down
2 changes: 1 addition & 1 deletion firmware/cmake/cangen.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cmake_language(GET_MESSAGE_LOG_LEVEL LOG_LEVEL)
# cangen is provided by racecar/scripts/cangen
add_custom_target(
generated_can
COMMAND "cangen" ${CMAKE_CURRENT_SOURCE_DIR} "--log-level=${LOG_LEVEL}"
COMMAND "cangen" ${CMAKE_CURRENT_SOURCE_DIR} "--cmake-log-level=${LOG_LEVEL}"
DEPENDS ${CAN_DEPENDENCIES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT Generating CAN code from DBCs
Expand Down
68 changes: 28 additions & 40 deletions scripts/cangen/cangen/can_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@
EIGHT_BITS = 8
EIGHT_BYTES = 8
TOTAL_BITS = EIGHT_BITS * EIGHT_BYTES
MSG_REGISTRY_FILE_NAME = "_msg_registry.h"
CAN_MESSAGES_FILE_NAME = "_can_messages.h"

CAN_MESSAGES_TEMPLATE_FILENAME = "can_messages.h.jinja2"
MSG_REGISTRY_TEMPLATE_FILENAME = "msg_registry.h.jinja2"
TEMPLATE_FILE_NAMES = ["can_messages.h.jinja2", "msg_registry.h.jinja2"]


def _parse_dbc_files(dbc_file: str) -> Database:
Expand Down Expand Up @@ -171,29 +168,12 @@ def _camel_to_snake(text):
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()


def _generate_from_jinja2_template(
template_path: str, output_path: str, context_dict: dict
):
# Create the environment with trim_blocks and lstrip_blocks settings
env = Environment(
loader=PackageLoader("cangen"), trim_blocks=True, lstrip_blocks=True
def _create_output_file_name(output_dir: str, bus_name: str, template_file_name: str) -> str:
return os.path.join(
output_dir, bus_name.lower() + "_" + template_file_name.removesuffix(".jinja2")
)

# Register the camel_to_snake filter
env.filters["camel_to_snake"] = _camel_to_snake
env.filters["decimal_to_hex"] = hex

# Load and render template
template = env.get_template(template_path)
rendered_code = template.render(**context_dict)

with open(output_path, "w") as output_file:
output_file.write(rendered_code)

logger.info(f"Rendered code written to '{os.path.abspath(output_path)}'")


def generate_code(bus: Bus, config: Config):
def _generate_code(bus: Bus, output_dir: str):
"""
Parses DBC files, extracts information, and generates code using Jinja2
templates.
Expand All @@ -220,39 +200,47 @@ def generate_code(bus: Bus, config: Config):
"bus_name": bus.bus_name,
}

logger.debug("Generating code for can messages")
_generate_from_jinja2_template(
CAN_MESSAGES_TEMPLATE_FILENAME,
os.path.join(config.output_dir, bus.bus_name.lower() + CAN_MESSAGES_FILE_NAME),
context,
)
logger.debug("Generating code for can messages and msg registry.")

logger.debug("Generating code for msg registry")
_generate_from_jinja2_template(
MSG_REGISTRY_TEMPLATE_FILENAME,
os.path.join(config.output_dir, bus.bus_name.lower() + MSG_REGISTRY_FILE_NAME),
context,
env = Environment(
loader=PackageLoader(__package__), trim_blocks=True, lstrip_blocks=True
)
env.filters["decimal_to_hex"] = hex
env.filters["camel_to_snake"] = _camel_to_snake

logger.info("Code generation complete")
for template_file_name in TEMPLATE_FILE_NAMES:
template = env.get_template(template_file_name)
rendered_code = template.render(**context)

output_file_name = _create_output_file_name(output_dir, bus.bus_name, template_file_name)
with open(output_file_name, "w") as output_file:
output_file.write(rendered_code)
logger.info(f"Rendered code written to '{os.path.abspath(output_file_name)}'")

def _prepare_output_directory(output_dir):
logger.info("Code generation complete")


def _prepare_output_directory(output_dir: str):
"""Deletes previously generated files and creates a gitignore for the directory"""
if os.path.exists(output_dir):
logger.info("Deleting previously generated code")
shutil.rmtree(output_dir)

logger.info("Creating generated/can folder")
os.makedirs(output_dir, exist_ok=True)

gitignore_path = os.path.join(output_dir, ".gitignore")
with open(gitignore_path, "w") as f:
f.write("*")


def generate_can_from_dbc(project_folder_name: str):
def generate_can_for_project(project_folder_name: str):
"""Generates C code for a given project.
Ensure the project folder contains a config.yaml file which specifies the relative path to its corresponding dbc file."""
os.chdir(project_folder_name)
config = Config.from_yaml("config.yaml")

_prepare_output_directory(config.output_dir)

for bus in config.busses:
generate_code(bus, config)
_generate_code(bus, config.output_dir)
39 changes: 27 additions & 12 deletions scripts/cangen/cangen/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import argparse
import logging

from .can_generator import generate_can_from_dbc
from .can_generator import generate_can_for_project

logging.basicConfig(level="INFO", format="%(levelname)-8s| (%(name)s) %(message)s")
logger = logging.getLogger(__name__)
Expand All @@ -15,28 +15,43 @@
def parse():
parser = argparse.ArgumentParser(description="DBC to C code generator")
parser.add_argument("project", type=str, help="Name of the project")
parser.add_argument(
"--log-level",
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-v",
"--verbose",
dest="is_verbose",
action="store_true",
help="Enable verbose output",
)
group.add_argument(
"--cmake-log-level",
dest="level",
choices=["STATUS", "INFO", "VERBOSE", "DEBUG"],
default="INFO",
help="Log verbosity threshold",
)

# If parsing fails (ex incorrect or no arguments provided) then this exits with
# code 2.
return parser.parse_args()


def main():
args = parse()
# If parsing fails (ex incorrect or no arguments provided) then this code will be 2
args = parser.parse_args()

cmake_to_python_level = {
"STATUS": "INFO",
"INFO": "INFO",
"VERBOSE": "DEBUG",
"DEBUG": "DEBUG",
}
logger.setLevel(cmake_to_python_level[args.level])

generate_can_from_dbc(args.project)
args.level = (
cmake_to_python_level["VERBOSE"]
if args.is_verbose
else cmake_to_python_level[args.level]
)
return args


def main():
args = parse()

logger.setLevel(args.level)

generate_can_for_project(args.project)

0 comments on commit ca8ae9d

Please sign in to comment.