-
Notifications
You must be signed in to change notification settings - Fork 41
Add flatbuffer support for COM configuration using flatbuffers #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
OliverHeilwagen
wants to merge
4
commits into
eclipse-score:main
Choose a base branch
from
etas-contrib:flatbuffer-configuration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
18805d6
COM configuration using flatbuffers - initial version.
OliverHeilwagen 75dcfcc
Add lola method configuration to flatbuffer config
OliverHeilwagen 524c211
Flatbuffers PR, fine tunings & cleanup
Thomas-Mikhael 6464d38
Merge branch 'main' into flatbuffer-configuration
Thomas-Mikhael File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,9 @@ | ||
| load(":json_schema_validator_test.bzl", "json_schema_validator_tests") | ||
|
|
||
| json_schema_validator_tests(name = "json_validator_tests") | ||
|
|
||
| py_binary( | ||
| name = "json_to_flatbuffer_json", | ||
| srcs = ["json_to_flatbuffer_json.py"], | ||
| visibility = ["//visibility:public"], | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """ | ||
| Bazel rule to generate FlatBuffer binary configuration from JSON files. | ||
|
|
||
| This rule converts existing communication JSON files to a FlatBuffer friendly format. | ||
| - Convert '-' to '_' in keys (required) | ||
| - Convert keys from camelCase to snake_case (avoids warnings) | ||
| """ | ||
|
|
||
| def generate_com_config(name, json, convert, visibility = None): | ||
| """ | ||
| Generate a FlatBuffer binary configuration file from a JSON input. | ||
|
|
||
| This rule can optionally convert the input JSON to FlatBuffer friendly format | ||
| before compiling to FlatBuffer binary format. | ||
|
|
||
| The schema is hardcoded to the COM FlatBuffer schema at: | ||
| //score/mw/com/impl/configuration:ara_com_config.fbs | ||
|
|
||
| The output .bin file will be generated in the same directory path as the | ||
| input JSON file to match the filegroup behavior for JSON files. | ||
|
|
||
| Args: | ||
| name: Name of the rule (will be used as the target name) | ||
| json: Input JSON configuration file | ||
| convert: Boolean - if True, converts JSON to FlatBuffer friendly format | ||
| (dash to underscore, camelCase to snake_case). If False, uses | ||
| JSON as-is (assumes it's already FlatBuffer conform) | ||
| visibility: Visibility for the generated target (optional) | ||
|
|
||
| Outputs: | ||
| A .bin file in the same directory as the input JSON | ||
| Example: "example/ara_com_config.json" -> "example/ara_com_config.bin" | ||
|
|
||
| Example: | ||
| generate_com_config( | ||
| name = "my_config.bin", | ||
| json = "example/config.json", | ||
| convert = True, | ||
| visibility = ["//visibility:public"], | ||
| ) | ||
| """ | ||
|
|
||
| # Always use the COM FlatBuffer schema | ||
| schema = "//score/mw/com/impl/configuration:ara_com_config.fbs" | ||
|
|
||
| # Extract base filepath and filename (remove .json extension if present) | ||
| if json.endswith(".json"): | ||
| filepath_base = json[:-5] | ||
| filename_base = json.split("/")[-1][:-5] | ||
| else: | ||
| filepath_base = json | ||
| filename_base = json.split("/")[-1] | ||
|
|
||
| # Preserve the full path of the JSON file, just change extension | ||
| output_path = filepath_base + ".bin" | ||
|
|
||
| if convert: | ||
| # Intermediate converted JSON file (keep in same directory) | ||
| converted_json = filepath_base + "_converted.json" | ||
|
|
||
| # Intermediate flatc output filename | ||
| flatc_output = filename_base + "_converted.bin" | ||
|
|
||
| # Step 1: Convert JSON to FlatBuffer friendly format | ||
| native.genrule( | ||
| name = name + "_convert", | ||
| srcs = [json], | ||
| outs = [converted_json], | ||
| tools = ["//bazel/tools:json_to_flatbuffer_json"], | ||
| cmd = "$(location //bazel/tools:json_to_flatbuffer_json) $(SRCS) $@", | ||
| visibility = ["//visibility:private"], | ||
| ) | ||
|
|
||
| # Step 2: Compile converted JSON to FlatBuffer binary | ||
| native.genrule( | ||
| name = name, | ||
| srcs = [converted_json, schema], | ||
| outs = [output_path], | ||
| tools = ["@flatbuffers//:flatc"], | ||
| cmd = "$(location @flatbuffers//:flatc) --binary -o $(@D) $(location " + schema + ") $(location " + converted_json + ") && mv $(@D)/" + flatc_output + " $@", | ||
| visibility = visibility, | ||
| ) | ||
| else: | ||
| # Intermediate flatc output filename | ||
| flatc_output = filename_base + ".bin" | ||
|
|
||
| # Only move if the output paths differ | ||
| if flatc_output == output_path.split("/")[-1]: | ||
| cmd = "$(location @flatbuffers//:flatc) --binary -o $(@D) $(location " + schema + ") $(location " + json + ")" | ||
| else: | ||
| cmd = "$(location @flatbuffers//:flatc) --binary -o $(@D) $(location " + schema + ") $(location " + json + ") && mv $(@D)/" + flatc_output + " $@" | ||
|
|
||
| native.genrule( | ||
| name = name, | ||
| srcs = [json, schema], | ||
| outs = [output_path], | ||
| tools = ["@flatbuffers//:flatc"], | ||
| cmd = cmd, | ||
| visibility = visibility, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Convert JSON with kebab-case keys to FlatBuffer-compatible JSON with snake_case keys. | ||
|
|
||
| This script recursively processes JSON objects and converts all kebab-case keys | ||
| (e.g., "asil-level") to snake_case (e.g., "asil_level") to match FlatBuffer field | ||
| naming conventions. | ||
| """ | ||
|
|
||
| import json | ||
| import sys | ||
| import re | ||
| from pathlib import Path | ||
| from typing import Any | ||
|
|
||
|
|
||
| def to_snake_case(text: str) -> str: | ||
| """ | ||
| Convert kebab-case or camelCase string to snake_case | ||
|
|
||
| Args: | ||
| text: String in kebab-case or camelCase format | ||
|
|
||
| Returns: | ||
| String in snake_case format | ||
| """ | ||
| result = text.replace('-', '_') | ||
|
|
||
| # Preserve consecutive uppercase letters (like ID, QM, B) | ||
| # Insert underscore before uppercase letter that follows lowercase letter | ||
| result = re.sub(r'([a-z])([A-Z])', r'\1_\2', result) | ||
| # Insert underscore before uppercase letter that is followed by lowercase | ||
| result = re.sub(r'([A-Z]+)([A-Z][a-z])', r'\1_\2', result) | ||
|
|
||
| return result.lower() | ||
|
|
||
|
|
||
| def convert_json(obj: Any) -> Any: | ||
| """ | ||
| Recursively converts all dictionary keys to snake_case and applies custom conversion | ||
| for specific string values which are implemented as enum in FlatBuffers. | ||
|
|
||
| Args: | ||
| obj: JSON object (dict, list, or primitive type) | ||
|
|
||
| Returns: | ||
| Converted JSON object | ||
| """ | ||
| if isinstance(obj, dict): | ||
| converted = {} | ||
| for key, value in obj.items(): | ||
| new_key = to_snake_case(key) | ||
| new_value = convert_enum_value(new_key, value) | ||
| converted[new_key] = convert_json(new_value) | ||
| return converted | ||
| elif isinstance(obj, list): | ||
| return [convert_json(item) for item in obj] | ||
| else: | ||
| return obj | ||
|
|
||
|
|
||
| def convert_enum_value(key: str, value: Any) -> Any: | ||
| """ | ||
| Convert communication JSON enum string values to FlatBuffer friendly format. | ||
|
|
||
| Args: | ||
| key: The field name | ||
| value: The field value | ||
|
|
||
| Returns: | ||
| Converted value | ||
| """ | ||
| if not isinstance(value, str): | ||
| return value | ||
|
|
||
| # Binding type: "SOME/IP" -> "SOME_IP", "SHM" -> "SHM" | ||
| if key == 'binding': | ||
| return value.replace('/', '_') | ||
|
|
||
| # Permission checks: "file-permissions-on-empty" -> "FILE_PERMISSIONS_ON_EMPTY", "strict" -> "STRICT" | ||
| if key == 'permission_checks': | ||
| return value.replace('-', '_').upper() | ||
|
|
||
| return value | ||
|
|
||
|
|
||
| def main(): | ||
| """Main entry point for the script.""" | ||
| if len(sys.argv) != 3: | ||
| print(f"Usage: {sys.argv[0]} <input.json> <output.json>", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
| input_file = Path(sys.argv[1]) | ||
| output_file = Path(sys.argv[2]) | ||
|
|
||
| # Validate input file exists | ||
| if not input_file.exists(): | ||
| print(f"Error: Input file '{input_file}' does not exist", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
| try: | ||
| with open(input_file, 'r', encoding='utf-8') as f: | ||
| data = json.load(f) | ||
|
|
||
| converted_data = convert_json(data) | ||
|
|
||
| with open(output_file, 'w', encoding='utf-8') as f: | ||
| json.dump(converted_data, f, indent=4, ensure_ascii=False) | ||
| f.write('\n') | ||
| print(f"Successfully converted {input_file} -> {output_file}") | ||
|
|
||
| except json.JSONDecodeError as e: | ||
| print(f"Error: Failed to parse JSON from '{input_file}': {e}", file=sys.stderr) | ||
| sys.exit(1) | ||
| except IOError as e: | ||
| print(f"Error: I/O error: {e}", file=sys.stderr) | ||
| sys.exit(1) | ||
| except Exception as e: | ||
| print(f"Error: Unexpected error: {e}", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| """ | ||
| Validate a JSON file against a FlatBuffer schema. | ||
|
|
||
| This rule converts existing communication JSON files to a FlatBuffer friendly format. | ||
| - Convert '-' to '_' in keys (required) | ||
| - Convert keys from camelCase to snake_case (avoids warnings) | ||
| """ | ||
|
|
||
| def _validate_json_flatbuffer_test_impl(ctx): | ||
| """Implementation of the validation test rule.""" | ||
|
|
||
| # Create a script that performs the validation | ||
| script = """#!/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| readonly expected_failure={expected_failure} | ||
| readonly converter='{converter}' | ||
| readonly flatc='{flatc}' | ||
| readonly schema='{schema}' | ||
| readonly json='{json}' | ||
| readonly tmpdir=$(mktemp -d) | ||
|
|
||
| cleanup() {{ | ||
| rm -rf "$tmpdir" | ||
| }} | ||
| trap cleanup EXIT | ||
|
|
||
| # Step 1: Convert JSON to FlatBuffer friendly format | ||
| "$converter" "$json" "$tmpdir/converted.json" | ||
|
|
||
| # Step 2: Validate with flatc by compiling to binary (validates structure) | ||
| # Capture both stdout and stderr, suppress output unless there's an error | ||
| set +e | ||
| output=$("$flatc" --binary -o "$tmpdir" "$schema" "$tmpdir/converted.json" 2>&1) | ||
| ret=$? | ||
| set -e | ||
|
|
||
| if test "$expected_failure" = true && test "$ret" -ne 0; then | ||
| echo "Expected validation to fail, and it did (exit code $ret)." | ||
| echo "" | ||
| echo "FlatBuffer validation errors:" | ||
| echo "$output" | ||
| echo "" | ||
| echo "Test PASSED." | ||
| exit 0 | ||
| elif test "$expected_failure" = false && test "$ret" -eq 0; then | ||
| echo "Expected validation to succeed, and it did. Test PASSED." | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Test failed - show what went wrong | ||
| if test "$ret" -ne 0; then | ||
| echo "FlatBuffer validation errors:" | ||
| echo "$output" | ||
| fi | ||
|
|
||
| echo "" | ||
| echo "Test FAILED: FlatBuffer validation of '$json' against '$schema' exited with code $ret, but expected_failure={expected_failure}" | ||
| exit 1 | ||
| """.format( | ||
| expected_failure = "true" if ctx.attr.expected_failure else "false", | ||
| converter = ctx.executable._converter.short_path, | ||
| flatc = ctx.executable._flatc.short_path, | ||
| schema = ctx.file.schema.short_path, | ||
| json = ctx.file.json.short_path, | ||
| ) | ||
|
|
||
| ctx.actions.write( | ||
| output = ctx.outputs.executable, | ||
| content = script, | ||
| is_executable = True, | ||
| ) | ||
|
|
||
| runfiles = ctx.runfiles( | ||
| files = [ctx.file.json, ctx.file.schema], | ||
| ).merge(ctx.attr._converter[DefaultInfo].default_runfiles) | ||
| runfiles = runfiles.merge(ctx.attr._flatc[DefaultInfo].default_runfiles) | ||
|
|
||
| return [DefaultInfo(runfiles = runfiles)] | ||
|
|
||
| validate_json_flatbuffer_test = rule( | ||
| implementation = _validate_json_flatbuffer_test_impl, | ||
| attrs = { | ||
| "json": attr.label( | ||
| allow_single_file = [".json"], | ||
| mandatory = True, | ||
| doc = "Input JSON file to validate", | ||
| ), | ||
| "schema": attr.label( | ||
| allow_single_file = [".fbs"], | ||
| mandatory = True, | ||
| doc = "FlatBuffer schema file (.fbs) to validate against", | ||
| ), | ||
| "expected_failure": attr.bool( | ||
| default = False, | ||
| doc = "If True, test passes when validation fails (for testing invalid inputs)", | ||
| ), | ||
| "_converter": attr.label( | ||
| default = Label("//bazel/tools:json_to_flatbuffer_json"), | ||
| executable = True, | ||
| cfg = "exec", | ||
| ), | ||
| "_flatc": attr.label( | ||
| default = Label("@flatbuffers//:flatc"), | ||
| executable = True, | ||
| cfg = "exec", | ||
| ), | ||
| }, | ||
| test = True, | ||
| doc = """ | ||
| Validates a JSON file against a FlatBuffer schema. | ||
| This rule converts existing communication JSON files to a FlatBuffer friendly format. | ||
|
|
||
| Example: | ||
| validate_json_flatbuffer_test( | ||
| name = "valid_config_test", | ||
| json = "valid_config.json", | ||
| schema = "ara_com_config.fbs", | ||
| ) | ||
|
|
||
| validate_json_flatbuffer_test( | ||
| name = "invalid_config_test", | ||
| json = "invalid_config.json", | ||
| schema = "ara_com_config.fbs", | ||
| expected_failure = True, | ||
| ) | ||
| """, | ||
| ) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Thomas-Mikhael: Please check all references in *.bzl, which is used outside of this module, we have to specify the full bazel path e.g.: