@@ -41,6 +41,115 @@ def gen_yml_rules(dir_name="rules", full_osi=False):
41
41
42
42
if not os .path .exists (dir_name ):
43
43
os .makedirs (dir_name )
44
+ if not os .path .exists (dir_name + "/schema" ):
45
+ os .makedirs (dir_name + "/schema" )
46
+
47
+ for file in glob ("open-simulation-interface/*.proto*" ):
48
+ filename = file .split ("open-simulation-interface/" )[1 ].split (".proto" )[0 ]
49
+
50
+ if os .path .exists (f"{ dir_name } /{ filename } .yml" ):
51
+ continue
52
+
53
+ with open (f"{ dir_name } /schema/{ filename } _schema.yml" , "a" ) as schema_file :
54
+ with open (file , "rt" ) as fin :
55
+ isEnum = False
56
+ numMessage = 0
57
+ saveStatement = ""
58
+ prevMainField = False # boolean, that the previous field has children
59
+
60
+ for line in fin :
61
+ if file .find (".proto" ) != - 1 :
62
+ # Search for comment ("//").
63
+ matchComment = re .search ("//" , line )
64
+ if matchComment is not None :
65
+ statement = line [: matchComment .start ()]
66
+ else :
67
+ statement = line
68
+
69
+ # Add part of the statement from last line.
70
+ statement = saveStatement + " " + statement
71
+
72
+ # New line is not necessary. Remove for a better output.
73
+ statement = statement .replace ("\n " , "" )
74
+
75
+ # Is statement complete
76
+ matchSep = re .search (r"[{};]" , statement )
77
+ if matchSep is None :
78
+ saveStatement = statement
79
+ statement = ""
80
+ else :
81
+ saveStatement = statement [matchSep .end () :]
82
+ statement = statement [: matchSep .end ()]
83
+
84
+ # Search for "enum".
85
+ matchEnum = re .search (r"\benum\b" , statement )
86
+ if matchEnum is not None :
87
+ isEnum = True
88
+
89
+ # Search for a closing brace.
90
+ matchClosingBrace = re .search ("}" , statement )
91
+ if isEnum is True and matchClosingBrace is not None :
92
+ isEnum = False
93
+ continue
94
+
95
+ # Check if not inside an enum.
96
+ if isEnum is False :
97
+ # Search for "message".
98
+ matchMessage = re .search (r"\bmessage\b" , statement )
99
+ if matchMessage is not None :
100
+ # a new message or a new nested message
101
+ numMessage += 1
102
+ endOfLine = statement [matchMessage .end () :]
103
+ matchName = re .search (r"\b\w[\S]*\b" , endOfLine )
104
+ if matchName is not None and prevMainField is False :
105
+ # Check previous main field to exclude empty fields from sensor specific file
106
+ matchNameConv = re .search (
107
+ r"\b[A-Z][a-zA-Z0-9]*\b" ,
108
+ endOfLine [matchName .start () : matchName .end ()],
109
+ )
110
+ schema_file .write (
111
+ 2 * (numMessage - 1 ) * " "
112
+ + f"{ matchNameConv .group (0 )} :\n "
113
+ )
114
+ prevMainField = True
115
+
116
+ elif re .search (r"\bextend\b" , statement ) is not None :
117
+ # treat extend as message
118
+ numMessage += 1
119
+
120
+ # Search for a closing brace.
121
+ matchClosingBrace = re .search ("}" , statement )
122
+ if numMessage > 0 and matchClosingBrace is not None :
123
+ numMessage -= 1
124
+
125
+ if matchComment is None and len (saveStatement ) == 0 :
126
+ if numMessage > 0 or isEnum == True :
127
+ if statement .find (";" ) != - 1 :
128
+ field = statement .strip ().split ()[2 ]
129
+ schema_file .write (
130
+ (2 * numMessage ) * " "
131
+ + f"{ field } : any(list(include('rules', required=False)), null(), required=False)\n "
132
+ )
133
+ prevMainField = False
134
+ schema_file .write (
135
+ "---\n "
136
+ "rules:\n "
137
+ " is_greater_than: num(required=False)\n "
138
+ " is_greater_than_or_equal_to: num(required=False)\n "
139
+ " is_less_than_or_equal_to: num(required=False)\n "
140
+ " is_less_than: num(required=False)\n "
141
+ " is_equal_to: any(num(), bool(), required=False)\n "
142
+ " is_different_to: num(required=False)\n "
143
+ " is_globally_unique: str(required=False)\n "
144
+ " refers_to: str(required=False)\n "
145
+ " is_iso_country_code: str(required=False)\n "
146
+ " is_set: str(required=False)\n "
147
+ " check_if: list(include('rules', required=False),required=False)\n "
148
+ " do_check: any(required=False)\n "
149
+ " target: any(required=False)\n "
150
+ " first_element: any(required=False)\n "
151
+ " last_element: any(required=False)"
152
+ )
44
153
45
154
for file in glob ("open-simulation-interface/*.proto*" ):
46
155
filename = file .split ("open-simulation-interface/" )[1 ].split (".proto" )[0 ]
@@ -54,6 +163,7 @@ def gen_yml_rules(dir_name="rules", full_osi=False):
54
163
numMessage = 0
55
164
shiftCounter = False
56
165
saveStatement = ""
166
+ prevMainField = False # boolean, that the previous field has children
57
167
rules = []
58
168
59
169
for line in fin :
@@ -120,7 +230,7 @@ def gen_yml_rules(dir_name="rules", full_osi=False):
120
230
numMessage += 1
121
231
endOfLine = statement [matchMessage .end () :]
122
232
matchName = re .search (r"\b\w[\S]*\b" , endOfLine )
123
- if matchName is not None :
233
+ if matchName is not None and prevMainField is False :
124
234
# Test case 10: Check name - no special char -
125
235
# start with a capital letter
126
236
matchNameConv = re .search (
@@ -132,6 +242,7 @@ def gen_yml_rules(dir_name="rules", full_osi=False):
132
242
2 * (numMessage - 1 ) * " "
133
243
+ f"{ matchNameConv .group (0 )} :\n "
134
244
)
245
+ prevMainField = True
135
246
136
247
elif re .search (r"\bextend\b" , statement ) is not None :
137
248
# treat extend as message
@@ -168,6 +279,8 @@ def gen_yml_rules(dir_name="rules", full_osi=False):
168
279
yml_file .write (
169
280
(2 * numMessage ) * " " + f"{ field } :\n "
170
281
)
282
+ prevMainField = False
283
+
171
284
# If option --full-osi is enabled:
172
285
# Check if is_set is already a rule for the current field, if not, add it.
173
286
if full_osi and not any (
@@ -233,6 +346,7 @@ def gen_yml_rules(dir_name="rules", full_osi=False):
233
346
(2 * numMessage + 8 ) * " "
234
347
+ f"- { rule_list [2 ]} : { rule_list [3 ]} \n "
235
348
)
349
+
236
350
# Standalone rules
237
351
elif any (
238
352
list_item
0 commit comments