diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 740ffa5d..0158bf7b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,6 +8,7 @@ repos: exclude_types: ["csv", "proto"] - id: end-of-file-fixer exclude_types: ["json", "proto"] + exclude: '.*.fidl' - id: check-yaml - id: check-added-large-files diff --git a/docs/vspec2x.md b/docs/vspec2x.md index 5ff55b84..0f888696 100644 --- a/docs/vspec2x.md +++ b/docs/vspec2x.md @@ -12,14 +12,14 @@ The supported arguments might look like this ``` usage: vspec2x.py [-h] [-I dir] [-e EXTENDED_ATTRIBUTES] [-s] [--abort-on-unknown-attribute] [--abort-on-name-style] - [--format format] [--uuid] [--no-uuid] [-o overlays] [-u unit_file] + [--format format] [--uuid] [--no-uuid] [--no_expand] [-o overlays] [-u unit_file] [-vt vspec_types_file] [-ot ] [--json-all-extended-attributes] [--json-pretty] [--yaml-all-extended-attributes] [-v version] [--all-idl-features] [--gqlfield GQLFIELD GQLFIELD] ``` -A common commandline to convert the VSS standard catalog into a JSON file is +An example command line to convert the VSS standard catalog into a JSON file is ``` % python vspec2x.py --format json -I ../spec -u ../spec/units.yaml ../spec/VehicleSignalSpecification.vspec vss.json @@ -68,6 +68,12 @@ This is currently the default behavior. From VSS 4.0 `--no-uuid` will be the def Request the exporter to not output uuids. From VSS 4.0 this will be the default behavior and then this parameter will be deprecated. +### --no-expand + +By default all tools expand instance information so that instance information like "Row1" become a branch just like +any other branch. If this argument is used and the exporter supports it no expansion will take place. +Instead instance information will be kept as additional information for the branch. + ## Handling of Data Types COVESA supports a number of pre-defined types, see [VSS documentation](https://covesa.github.io/vehicle_signal_specification/rule_set/data_entry/data_types/). @@ -239,6 +245,10 @@ signal and not expand it further. If using an overlay to redefine a specific sig (like `Vehicle.Cabin.Door.Row1.Left.IsChildLockActive`) has precedence over data specified for not yet extended signals (like `Vehicle.Cabin.Door.IsChildLockActive`). +*Note: If using `--no-expand` together with overlays for specific instances then the result will be a combination* +*of expanded and unexpanded paths. For the example above `Vehicle.Cabin.Door.Row1.Left.NewSignal` will be expanded* +*but all other signals in `Vehicle.Cabin.Door` will remain unexpanded!* + It is possible to use `-o` multiple times, e.g. ``` diff --git a/docs/vspec2x_arch.md b/docs/vspec2x_arch.md index fcfc75c0..fbc69881 100644 --- a/docs/vspec2x_arch.md +++ b/docs/vspec2x_arch.md @@ -19,6 +19,9 @@ This will result in that e.g. `Vehicle.Cabin.Door` is expanded to the following * `Vehicle.Cabin.Door.Row2.Left` * `Vehicle.Cabin.Door.Row2.Right` +For some exporters expansion can be suppressed by using the `--no_expand` option. +Then instance information will be represented by other means in the resulting output. + ## Expansion and Overlays Sometimes an overlay only refers to a signal in a specific branch, like: diff --git a/tests/vspec/test_no_expand/expected.csv b/tests/vspec/test_no_expand/expected.csv new file mode 100644 index 00000000..31612e15 --- /dev/null +++ b/tests/vspec/test_no_expand/expected.csv @@ -0,0 +1,10 @@ +"Signal","Type","DataType","Deprecated","Unit","Min","Max","Desc","Comment","Allowed","Default","Instances" +"A","branch","","","","","","Branch A.","","","" +"A.B","branch","","","","","","Branch with explicit instances.","","","","[['Test1', 'Test2', 'Test3']]" +"A.B.S","sensor","int8","","km","","","Signal A.B.S.","","","" +"A.C","branch","","","","","","Branch with instance range.","","","","Test[1,4]" +"A.C.S","sensor","int8","","km","","","Signal A.C.S.","","","" +"A.D","branch","","","","","","Branch with complex instance combination.","","","","[['Test1'], 'Test[2,3]', ['Test4', 'Test5', 'Test6'], ['Test7', 'Test8', 'Test9', 'Test10'], ['Test11']]" +"A.D.S","sensor","int8","","km","","","Signal A.D.S.","","","" +"A.E","branch","","","","","","Branch without instances for reference.","","","" +"A.E.S","sensor","int8","","km","","","Signal A.E.S.","","","" diff --git a/tests/vspec/test_no_expand/expected.fidl b/tests/vspec/test_no_expand/expected.fidl new file mode 100644 index 00000000..fb569f4e --- /dev/null +++ b/tests/vspec/test_no_expand/expected.fidl @@ -0,0 +1,62 @@ + +// Copyright (C) 2022, COVESA +// +// This program is licensed under the terms and conditions of the +// Mozilla Public License, version 2.0. The full text of the +// Mozilla Public License is at https://www.mozilla.org/MPL/2.0/ + +const UTF8String VSS_VERSION = "None" + +struct SignalSpec { + UInt32 id + String name + String type + String description + String datatype + String unit + Double min + Double max +} + +const SignalSpec[] signal_spec = [ +{ name: "A.B", + type: "branch", + description: "Branch with explicit instances." +}, +{ name: "A.B.S", + type: "sensor", + description: "Signal A.B.S.", + datatype: "int8", + unit: "km" +}, +{ name: "A.C", + type: "branch", + description: "Branch with instance range." +}, +{ name: "A.C.S", + type: "sensor", + description: "Signal A.C.S.", + datatype: "int8", + unit: "km" +}, +{ name: "A.D", + type: "branch", + description: "Branch with complex instance combination." +}, +{ name: "A.D.S", + type: "sensor", + description: "Signal A.D.S.", + datatype: "int8", + unit: "km" +}, +{ name: "A.E", + type: "branch", + description: "Branch without instances for reference." +}, +{ name: "A.E.S", + type: "sensor", + description: "Signal A.E.S.", + datatype: "int8", + unit: "km" +} +] \ No newline at end of file diff --git a/tests/vspec/test_no_expand/expected.graphql b/tests/vspec/test_no_expand/expected.graphql new file mode 100644 index 00000000..e250d59a --- /dev/null +++ b/tests/vspec/test_no_expand/expected.graphql @@ -0,0 +1,98 @@ +type Query { + vehicle( + """VIN of the vehicle that you want to request data for.""" + id: String! + + """ + Filter data to only provide information that was sent from the vehicle after that timestamp. + """ + after: String + ): A +} + +"""Branch A.""" +type A { + """Branch with explicit instances.""" + b: A_B + + """Branch with instance range.""" + c: A_C + + """Branch with complex instance combination.""" + d: A_D + + """Branch without instances for reference.""" + e: A_E +} + +"""Branch with explicit instances.""" +type A_B { + """Signal A.B.S.""" + s: A_B_S +} + +"""Signal A.B.S.""" +type A_B_S { + """Value: Signal A.B.S.""" + value: Int + + """Timestamp: Signal A.B.S.""" + timestamp: String + + """Unit of Signal A.B.S.""" + unit: String +} + +"""Branch with instance range.""" +type A_C { + """Signal A.C.S.""" + s: A_C_S +} + +"""Signal A.C.S.""" +type A_C_S { + """Value: Signal A.C.S.""" + value: Int + + """Timestamp: Signal A.C.S.""" + timestamp: String + + """Unit of Signal A.C.S.""" + unit: String +} + +"""Branch with complex instance combination.""" +type A_D { + """Signal A.D.S.""" + s: A_D_S +} + +"""Signal A.D.S.""" +type A_D_S { + """Value: Signal A.D.S.""" + value: Int + + """Timestamp: Signal A.D.S.""" + timestamp: String + + """Unit of Signal A.D.S.""" + unit: String +} + +"""Branch without instances for reference.""" +type A_E { + """Signal A.E.S.""" + s: A_E_S +} + +"""Signal A.E.S.""" +type A_E_S { + """Value: Signal A.E.S.""" + value: Int + + """Timestamp: Signal A.E.S.""" + timestamp: String + + """Unit of Signal A.E.S.""" + unit: String +} diff --git a/tests/vspec/test_no_expand/expected.idl b/tests/vspec/test_no_expand/expected.idl new file mode 100644 index 00000000..7a9b5aff --- /dev/null +++ b/tests/vspec/test_no_expand/expected.idl @@ -0,0 +1,47 @@ +module A +{ +module B +{ +struct S +{ +octet value; +//const string unit="km"; +//const string type ="sensor"; +//const string description="Signal A.B.S."; +}; +}; + +module C +{ +struct S +{ +octet value; +//const string unit="km"; +//const string type ="sensor"; +//const string description="Signal A.C.S."; +}; +}; + +module D +{ +struct S +{ +octet value; +//const string unit="km"; +//const string type ="sensor"; +//const string description="Signal A.D.S."; +}; +}; + +module E +{ +struct S +{ +octet value; +//const string unit="km"; +//const string type ="sensor"; +//const string description="Signal A.E.S."; +}; +}; + +}; diff --git a/tests/vspec/test_no_expand/expected.json b/tests/vspec/test_no_expand/expected.json new file mode 100644 index 00000000..8314ba51 --- /dev/null +++ b/tests/vspec/test_no_expand/expected.json @@ -0,0 +1,84 @@ +{ + "A": { + "children": { + "B": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.B.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with explicit instances.", + "instances": [ + [ + "Test1", + "Test2", + "Test3" + ] + ], + "type": "branch" + }, + "C": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with instance range.", + "instances": "Test[1,4]", + "type": "branch" + }, + "D": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "instances": [ + [ + "Test1" + ], + "Test[2,3]", + [ + "Test4", + "Test5", + "Test6" + ], + [ + "Test7", + "Test8", + "Test9", + "Test10" + ], + [ + "Test11" + ] + ], + "type": "branch" + }, + "E": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.E.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch without instances for reference.", + "type": "branch" + } + }, + "description": "Branch A.", + "type": "branch" + } +} \ No newline at end of file diff --git a/tests/vspec/test_no_expand/expected.proto b/tests/vspec/test_no_expand/expected.proto new file mode 100644 index 00000000..19d240a5 --- /dev/null +++ b/tests/vspec/test_no_expand/expected.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + + +message A { + AB B = 1; + AC C = 2; + AD D = 3; + AE E = 4; +} + +message AB { + int32 S = 1; +} + +message AC { + int32 S = 1; +} + +message AD { + int32 S = 1; +} + +message AE { + int32 S = 1; +} + diff --git a/tests/vspec/test_no_expand/expected.yaml b/tests/vspec/test_no_expand/expected.yaml new file mode 100644 index 00000000..dfda8a38 --- /dev/null +++ b/tests/vspec/test_no_expand/expected.yaml @@ -0,0 +1,59 @@ +A: + description: Branch A. + type: branch + +A.B: + description: Branch with explicit instances. + instances: + - - Test1 + - Test2 + - Test3 + type: branch + +A.B.S: + datatype: int8 + description: Signal A.B.S. + type: sensor + unit: km + +A.C: + description: Branch with instance range. + instances: Test[1,4] + type: branch + +A.C.S: + datatype: int8 + description: Signal A.C.S. + type: sensor + unit: km + +A.D: + description: Branch with complex instance combination. + instances: + - - Test1 + - Test[2,3] + - - Test4 + - Test5 + - Test6 + - - Test7 + - Test8 + - Test9 + - Test10 + - - Test11 + type: branch + +A.D.S: + datatype: int8 + description: Signal A.D.S. + type: sensor + unit: km + +A.E: + description: Branch without instances for reference. + type: branch + +A.E.S: + datatype: int8 + description: Signal A.E.S. + type: sensor + unit: km diff --git a/tests/vspec/test_no_expand/expected_overlay_expand.json b/tests/vspec/test_no_expand/expected_overlay_expand.json new file mode 100644 index 00000000..8b96904c --- /dev/null +++ b/tests/vspec/test_no_expand/expected_overlay_expand.json @@ -0,0 +1,632 @@ +{ + "A": { + "children": { + "B": { + "children": { + "Test1": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.B.S.", + "type": "sensor", + "unit": "km" + }, + "X": { + "datatype": "int8", + "description": "Adding signal A.B.X for all A.B instances in overlay", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with explicit instances.", + "type": "branch" + }, + "Test2": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.B.S.", + "type": "sensor", + "unit": "km" + }, + "X": { + "datatype": "int8", + "description": "Adding signal A.B.X for all A.B instances in overlay", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with explicit instances.", + "type": "branch" + }, + "Test3": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.B.S.", + "type": "sensor", + "unit": "km" + }, + "X": { + "datatype": "int8", + "description": "Adding signal A.B.X for all A.B instances in overlay", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with explicit instances.", + "type": "branch" + } + }, + "description": "Branch with explicit instances.", + "type": "branch" + }, + "C": { + "children": { + "Test1": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with instance range.", + "type": "branch" + }, + "Test2": { + "children": { + "S": { + "datatype": "float", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "We must explicitly define this instance branch in the overlay", + "type": "branch" + }, + "Test3": { + "children": { + "NewSignal": { + "datatype": "float", + "description": "A new signal only existing for one instance", + "type": "sensor", + "unit": "km" + }, + "S": { + "datatype": "int8", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "We must explicitly define this instance branch in the overlay", + "type": "branch" + }, + "Test4": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with instance range.", + "type": "branch" + } + }, + "description": "Branch with instance range.", + "type": "branch" + }, + "D": { + "children": { + "Test1": { + "children": { + "Test2": { + "children": { + "Test4": { + "children": { + "Test10": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test7": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test8": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test9": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test5": { + "children": { + "Test10": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test7": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test8": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test9": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test6": { + "children": { + "Test10": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test7": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test8": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test9": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test3": { + "children": { + "Test4": { + "children": { + "Test10": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test7": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test8": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test9": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test5": { + "children": { + "Test10": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test7": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test8": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test9": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test6": { + "children": { + "Test10": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test7": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test8": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "Test9": { + "children": { + "Test11": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + } + }, + "description": "Branch with complex instance combination.", + "type": "branch" + }, + "E": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.E.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch without instances for reference.", + "type": "branch" + } + }, + "description": "Branch A.", + "type": "branch" + } +} \ No newline at end of file diff --git a/tests/vspec/test_no_expand/expected_overlay_no_expand.json b/tests/vspec/test_no_expand/expected_overlay_no_expand.json new file mode 100644 index 00000000..a3c5827d --- /dev/null +++ b/tests/vspec/test_no_expand/expected_overlay_no_expand.json @@ -0,0 +1,114 @@ +{ + "A": { + "children": { + "B": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.B.S.", + "type": "sensor", + "unit": "km" + }, + "X": { + "datatype": "int8", + "description": "Adding signal A.B.X for all A.B instances in overlay", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with explicit instances.", + "instances": [ + [ + "Test1", + "Test2", + "Test3" + ] + ], + "type": "branch" + }, + "C": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + }, + "Test2": { + "children": { + "S": { + "datatype": "float", + "description": "Signal A.C.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "We must explicitly define this instance branch in the overlay", + "type": "branch" + }, + "Test3": { + "children": { + "NewSignal": { + "datatype": "float", + "description": "A new signal only existing for one instance", + "type": "sensor", + "unit": "km" + } + }, + "description": "We must explicitly define this instance branch in the overlay", + "type": "branch" + } + }, + "description": "Branch with instance range.", + "instances": "Test[1,4]", + "type": "branch" + }, + "D": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.D.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch with complex instance combination.", + "instances": [ + [ + "Test1" + ], + "Test[2,3]", + [ + "Test4", + "Test5", + "Test6" + ], + [ + "Test7", + "Test8", + "Test9", + "Test10" + ], + [ + "Test11" + ] + ], + "type": "branch" + }, + "E": { + "children": { + "S": { + "datatype": "int8", + "description": "Signal A.E.S.", + "type": "sensor", + "unit": "km" + } + }, + "description": "Branch without instances for reference.", + "type": "branch" + } + }, + "description": "Branch A.", + "type": "branch" + } +} \ No newline at end of file diff --git a/tests/vspec/test_no_expand/overlay.vspec b/tests/vspec/test_no_expand/overlay.vspec new file mode 100644 index 00000000..2ff8189e --- /dev/null +++ b/tests/vspec/test_no_expand/overlay.vspec @@ -0,0 +1,34 @@ + +# Adding signals for all instances is not affected, A.B.X is represented just as A.B.S in the output + +A.B.X: + datatype: int8 + type: sensor + unit: km + description: Adding signal A.B.X for all A.B instances in overlay + +# When using "--no-expand" and overlays on specific instances we must explicitly create the instance-branches +# as they are not created by expansion + +A.C.Test2: + type: branch + description: We must explicitly define this instance branch in the overlay + +# Change type for one instance +A.C.Test2.S: + datatype: float + type: sensor + unit: km + description: Signal A.C.S. + + +A.C.Test3: + type: branch + description: We must explicitly define this instance branch in the overlay + +# Add a signal for instance Test3 +A.C.Test3.NewSignal: + datatype: float + type: sensor + unit: km + description: A new signal only existing for one instance diff --git a/tests/vspec/test_no_expand/test.vspec b/tests/vspec/test_no_expand/test.vspec new file mode 100644 index 00000000..24c8c256 --- /dev/null +++ b/tests/vspec/test_no_expand/test.vspec @@ -0,0 +1,53 @@ +# +A: + type: branch + description: Branch A. + +A.B: + type: branch + instances: + - ["Test1","Test2","Test3"] + description: Branch with explicit instances. + +A.B.S: + datatype: int8 + type: sensor + unit: km + description: Signal A.B.S. + +A.C: + type: branch + instances: Test[1,4] + description: Branch with instance range. + +A.C.S: + datatype: int8 + type: sensor + unit: km + description: Signal A.C.S. + +A.D: + type: branch + instances: + - ["Test1"] + - Test[2,3] + - ["Test4", "Test5", "Test6"] + - ["Test7", "Test8", "Test9", "Test10"] + - ["Test11"] + description: Branch with complex instance combination. + +A.D.S: + datatype: int8 + type: sensor + unit: km + description: Signal A.D.S. + +A.E: + type: branch + description: Branch without instances for reference. + +A.E.S: + datatype: int8 + type: sensor + unit: km + description: Signal A.E.S. diff --git a/tests/vspec/test_no_expand/test_no_expand.py b/tests/vspec/test_no_expand/test_no_expand.py new file mode 100644 index 00000000..f80a05c1 --- /dev/null +++ b/tests/vspec/test_no_expand/test_no_expand.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +# +# (C) 2023 Robert Bosch GmbH +# +# All files and artifacts in this repository are licensed under the +# provisions of the license provided by the LICENSE file in this repository. +# + +import pytest +import os + + +# #################### Helper methods ############################# + +@pytest.fixture +def change_test_dir(request, monkeypatch): + # To make sure we run from test directory + monkeypatch.chdir(request.fspath.dirname) + + +@pytest.mark.parametrize("format, output_file, comparison_file, is_warning_expected", [ + ('json', 'out.json', 'expected.json', False), + ('yaml', 'out.yaml', 'expected.yaml', False), + ('csv', 'out.csv', 'expected.csv', False), + ('protobuf', 'out.proto', 'expected.proto', True), + ('idl', 'out.idl', 'expected.idl', True), + ('franca', 'out.fidl', 'expected.fidl', True), + ('graphql', 'out.graphql', 'expected.graphql', True)]) +def test_no_expand(format, output_file, comparison_file, is_warning_expected: bool, change_test_dir): + + args = ["../../../vspec2x.py", "--no-expand", "--format", format] + if format == 'json': + args.append('--json-pretty') + args.extend(["-u", "../test_units.yaml", + "test.vspec", output_file, "1>", "out.txt", "2>&1"]) + test_str = " ".join(args) + + result = os.system(test_str) + os.system("cat out.txt") + assert os.WIFEXITED(result) + assert os.WEXITSTATUS(result) == 0 + + # For exporters not supporting "no-expand" a warning shall be given + test_str = 'grep \"no_expand not supported by exporter\" out.txt > /dev/null' + result = os.system(test_str) + assert os.WIFEXITED(result) + if is_warning_expected: + assert os.WEXITSTATUS(result) == 0 + else: + assert os.WEXITSTATUS(result) == 1 + + test_str = f"diff {output_file} {comparison_file}" + result = os.system(test_str) + os.system("rm -f out.txt") + assert os.WIFEXITED(result) + assert os.WEXITSTATUS(result) == 0 + + os.system(f"rm -f {output_file}") + +# Overlay tests, just showing for JSON + + +@pytest.mark.parametrize("no_expand, comparison_file", [ + (False, 'expected_overlay_expand.json'), + (True, 'expected_overlay_no_expand.json')]) +def test_json_overlay(no_expand, comparison_file, change_test_dir): + """Test with overlay and expansion (for reference/comparison)""" + args = ["../../../vspec2x.py"] + + if no_expand: + args.append('--no-expand') + + args.extend(["--format", "json", "--json-pretty", "-u", "../test_units.yaml", + "test.vspec", "-o", "overlay.vspec", "out.json", "1>", "out.txt", "2>&1"]) + test_str = " ".join(args) + + result = os.system(test_str) + os.system("cat out.txt") + assert os.WIFEXITED(result) + assert os.WEXITSTATUS(result) == 0 + + test_str = f"diff out.json {comparison_file}" + result = os.system(test_str) + os.system("rm -f out.txt") + assert os.WIFEXITED(result) + assert os.WEXITSTATUS(result) == 0 + + os.system("rm -f out.json") diff --git a/vspec/model/vsstree.py b/vspec/model/vsstree.py index 4c33918c..4eec05ed 100644 --- a/vspec/model/vsstree.py +++ b/vspec/model/vsstree.py @@ -57,6 +57,7 @@ class VSSNode(Node): default = "" instances = None + expanded = False deprecation = "" def __deepcopy__(self, memo): diff --git a/vspec/vssexporters/vss2binary.py b/vspec/vssexporters/vss2binary.py index c5105455..059c52c6 100644 --- a/vspec/vssexporters/vss2binary.py +++ b/vspec/vssexporters/vss2binary.py @@ -18,6 +18,11 @@ _cbinary = None +def feature_supported(feature_name: str): + """Return true for supported optional arguments/features""" + return False + + def createBinaryCnode(fname, nodename, nodetype, uuid, description, nodedatatype, nodemin, nodemax, unit, allowed, defaultAllowed, validate, children): global _cbinary diff --git a/vspec/vssexporters/vss2csv.py b/vspec/vssexporters/vss2csv.py index d3399529..9847e035 100644 --- a/vspec/vssexporters/vss2csv.py +++ b/vspec/vssexporters/vss2csv.py @@ -18,17 +18,26 @@ from typing import AnyStr +def feature_supported(feature_name: str): + """Return true for supported arguments/features""" + if feature_name in ['no_expand']: + return True + return False + + def add_arguments(parser: argparse.ArgumentParser): parser.description = "The csv exporter does not support any additional arguments." # Write the header line -def print_csv_header(file, uuid, entry_type: AnyStr): +def print_csv_header(file, uuid, entry_type: AnyStr, include_instance_column: bool): arg_list = [entry_type, "Type", "DataType", "Deprecated", "Unit", "Min", "Max", "Desc", "Comment", "Allowed", "Default"] if uuid: arg_list.append("Id") + if include_instance_column: + arg_list.append("Instances") file.write(format_csv_line(arg_list)) # Format a data or header line according to the CSV standard (IETF RFC 4180) @@ -43,7 +52,7 @@ def format_csv_line(csv_fields): # Write the data lines -def print_csv_content(file, tree: VSSNode, uuid): +def print_csv_content(file, tree: VSSNode, uuid, include_instance_column: bool): tree_node: VSSNode for tree_node in PreOrderIter(tree): data_type_str = tree_node.get_datatype() @@ -53,6 +62,8 @@ def print_csv_content(file, tree: VSSNode, uuid): tree_node.allowed, tree_node.default] if uuid: arg_list.append(tree_node.uuid) + if include_instance_column and tree_node.instances is not None: + arg_list.append(tree_node.instances) file.write(format_csv_line(arg_list)) @@ -63,15 +74,15 @@ def export(config: argparse.Namespace, signal_root: VSSNode, print_uuid, data_ty generic_entry = data_type_root is not None and config.types_output_file is None with open(config.output_file, 'w') as f: signal_entry_type = "Node" if generic_entry else "Signal" - print_csv_header(f, print_uuid, signal_entry_type) - print_csv_content(f, signal_root, print_uuid) + print_csv_header(f, print_uuid, signal_entry_type, config.no_expand) + print_csv_content(f, signal_root, print_uuid, config.no_expand) if data_type_root is not None and generic_entry is True: - print_csv_content(f, data_type_root, print_uuid) + print_csv_content(f, data_type_root, print_uuid, config.no_expand) if data_type_root is not None and generic_entry is False: with open(config.types_output_file, 'w') as f: - print_csv_header(f, print_uuid, "Node") - print_csv_content(f, data_type_root, print_uuid) + print_csv_header(f, print_uuid, "Node", config.no_expand) + print_csv_content(f, data_type_root, print_uuid, config.no_expand) if __name__ == "__main__": diff --git a/vspec/vssexporters/vss2ddsidl.py b/vspec/vssexporters/vss2ddsidl.py index 197576a5..b8240702 100644 --- a/vspec/vssexporters/vss2ddsidl.py +++ b/vspec/vssexporters/vss2ddsidl.py @@ -16,6 +16,11 @@ from vspec.model.vsstree import VSSNode, VSSType +def feature_supported(feature_name: str): + """Return true for supported optional arguments/features""" + return False + + def add_arguments(parser: argparse.ArgumentParser): parser.description = "The DDS-IDL exporter" parser.add_argument('--all-idl-features', action='store_true', diff --git a/vspec/vssexporters/vss2franca.py b/vspec/vssexporters/vss2franca.py index 88d5ec78..1a59e402 100644 --- a/vspec/vssexporters/vss2franca.py +++ b/vspec/vssexporters/vss2franca.py @@ -14,6 +14,11 @@ from anytree import PreOrderIter # type: ignore[import] +def feature_supported(feature_name: str): + """Return true for supported optional arguments/features""" + return False + + def add_arguments(parser: argparse.ArgumentParser): # no additional output for Franca at this moment parser.add_argument('-v', metavar='version', help=" Add version information to franca file.") diff --git a/vspec/vssexporters/vss2graphql.py b/vspec/vssexporters/vss2graphql.py index 3578541d..fe829dc8 100644 --- a/vspec/vssexporters/vss2graphql.py +++ b/vspec/vssexporters/vss2graphql.py @@ -56,6 +56,11 @@ } +def feature_supported(feature_name: str): + """Return true for supported optional arguments/features""" + return False + + def add_arguments(parser: argparse.ArgumentParser): # no additional output for graphql at this moment parser.description = "The graphql exporter never generates uuid, i.e. the --uuid option has no effect." diff --git a/vspec/vssexporters/vss2json.py b/vspec/vssexporters/vss2json.py index c1d72674..b32691dc 100644 --- a/vspec/vssexporters/vss2json.py +++ b/vspec/vssexporters/vss2json.py @@ -16,6 +16,13 @@ from vspec.loggingconfig import initLogging +def feature_supported(feature_name: str): + """Return true for supported arguments/features""" + if feature_name in ['no_expand']: + return True + return False + + def add_arguments(parser: argparse.ArgumentParser): parser.add_argument('--json-all-extended-attributes', action='store_true', help="Generate all extended attributes found in the model " @@ -67,6 +74,10 @@ def export_node(json_dict, node, config, print_uuid): continue json_dict[node.name][k] = v + # Include instance information if we run tool in "no-expand" mode + if config.no_expand and node.instances is not None: + json_dict[node.name]["instances"] = node.instances + # Might be better to not generate child dict, if there are no children # if node.type == VSSType.BRANCH and len(node.children) != 0: # json_dict[node.name]["children"]={} diff --git a/vspec/vssexporters/vss2protobuf.py b/vspec/vssexporters/vss2protobuf.py index 3770cc7f..99d21d07 100755 --- a/vspec/vssexporters/vss2protobuf.py +++ b/vspec/vssexporters/vss2protobuf.py @@ -36,6 +36,11 @@ } +def feature_supported(feature_name: str): + """Return true for supported optional arguments/features""" + return False + + def add_arguments(parser: argparse.ArgumentParser): parser.description = "The protobuf exporter does not support any additional arguments." diff --git a/vspec/vssexporters/vss2yaml.py b/vspec/vssexporters/vss2yaml.py index c8bfd8ac..ed37cf21 100644 --- a/vspec/vssexporters/vss2yaml.py +++ b/vspec/vssexporters/vss2yaml.py @@ -22,6 +22,13 @@ from typing import Dict, Any +def feature_supported(feature_name: str): + """Return true for supported arguments/features""" + if feature_name in ['no_expand']: + return True + return False + + def add_arguments(parser: argparse.ArgumentParser): parser.add_argument('--yaml-all-extended-attributes', action='store_true', help=("Generate all extended attributes found in the model " @@ -75,6 +82,10 @@ def export_node(yaml_dict, node, config, print_uuid): continue yaml_dict[node_path][k] = v + # Include instance information if we run tool in "no-expand" mode + if config.no_expand and node.instances is not None: + yaml_dict[node_path]["instances"] = node.instances + for child in node.children: export_node(yaml_dict, child, config, print_uuid) diff --git a/vspec2x.py b/vspec2x.py index 36ce3353..1619fe6a 100755 --- a/vspec2x.py +++ b/vspec2x.py @@ -79,6 +79,8 @@ def main(arguments): ". If omitted we try to guess form output_file suffix.") parser.add_argument('--uuid', action='store_true', help='Include uuid in generated files.') + parser.add_argument('--no-expand', action='store_true', + help='Do not expand tree.') parser.add_argument('--no-uuid', action='store_true', help='Exclude uuid in generated files. This is currently the default behavior. ' + ' This argument is deprecated and will be removed in VSS 5.0') @@ -158,6 +160,10 @@ def main(arguments): vspec.load_units(args.vspec_file, args.unit_file) + # Warn if unsupported feature is used + if args.no_expand and not exporter.feature_supported("no_expand"): + logging.warning("--no_expand not supported by exporter") + # process data type tree if args.types_output_file is not None and not args.vspec_types_file: parser.error("An output file for data types was provided. Please also provide " @@ -183,7 +189,8 @@ def main(arguments): vspec.merge_tree(tree, othertree) vspec.check_type_usage(tree, VSSTreeType.SIGNAL_TREE, data_type_tree) - vspec.expand_tree_instances(tree) + if not args.no_expand: + vspec.expand_tree_instances(tree) vspec.clean_metadata(tree) vspec.verify_mandatory_attributes(tree, abort_on_unknown_attribute)