statable 3.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codegen/__init__.py +38 -0
- codegen/c_code_generator.py +1752 -0
- codegen/code_merger.py +371 -0
- codegen/code_templates.py +509 -0
- codegen/config.py +167 -0
- codegen/enum_generator.py +377 -0
- codegen/event_queue_generator.py +303 -0
- codegen/interrupt_generator.py +345 -0
- codegen/naming_convention.py +171 -0
- codegen/osal_generator.py +282 -0
- codegen/role_function_generator.py +1244 -0
- codegen/sample_data.py +269 -0
- codegen/state_actions_generator.py +395 -0
- codegen/struct_generator.py +721 -0
- codegen/timer_generator.py +299 -0
- codegen/transition_generator.py +907 -0
- codegen/type_mapper.py +69 -0
- codegen/validate/__init__.py +38 -0
- codegen/validate/change_actions.py +90 -0
- codegen/validate/change_applier.py +416 -0
- codegen/validate/clipboard_manager.py +47 -0
- codegen/validate/data/__init__.py +30 -0
- codegen/validate/data/action_definitions.py +200 -0
- codegen/validate/data/keywords.py +46 -0
- codegen/validate/data/prompt_templates.py +176 -0
- codegen/validate/data/validation_rules.py +205 -0
- codegen/validate/items/__init__.py +30 -0
- codegen/validate/items/base_validator.py +25 -0
- codegen/validate/items/cell_validator.py +275 -0
- codegen/validate/items/custom_type_validator.py +75 -0
- codegen/validate/items/event_validator.py +78 -0
- codegen/validate/items/flag_validator.py +74 -0
- codegen/validate/items/interrupt_validator.py +75 -0
- codegen/validate/items/queue_validator.py +73 -0
- codegen/validate/items/role_function_validator.py +175 -0
- codegen/validate/items/state_validator.py +88 -0
- codegen/validate/items/timer_validator.py +120 -0
- codegen/validate/items/transition_validator.py +113 -0
- codegen/validate/items/variable_validator.py +132 -0
- codegen/validate/logger.py +52 -0
- codegen/validate/models.py +177 -0
- codegen/validate/prompt_generator.py +724 -0
- codegen/validate/response_parser.py +154 -0
- codegen/validate/response_validator.py +275 -0
- codegen/validate/validation_dialog.py +322 -0
- codegen/validate/validator.py +94 -0
- codegen/variable_generator.py +419 -0
- statable/__init__.py +71 -0
- statable/__main__.py +49 -0
- statable/cli.py +251 -0
- statable/global_defs.py +256 -0
- statable/mermaid_gen.py +144 -0
- statable/model.py +426 -0
- statable/parser.py +64 -0
- statable/sample_data.py +435 -0
- statable/shared/__init__.py +24 -0
- statable/shared/condition_library.py +63 -0
- statable/shared/literal_library.py +66 -0
- statable/shared/role_function_library.py +150 -0
- statable/state_machine.py +121 -0
- statable/xml_io.py +1164 -0
- statable-3.1.0.dist-info/METADATA +151 -0
- statable-3.1.0.dist-info/RECORD +117 -0
- statable-3.1.0.dist-info/WHEEL +5 -0
- statable-3.1.0.dist-info/entry_points.txt +2 -0
- statable-3.1.0.dist-info/top_level.txt +3 -0
- statable_gui/__init__.py +23 -0
- statable_gui/action_edit_dialog.py +214 -0
- statable_gui/code_generation_dialog.py +486 -0
- statable_gui/code_generation_settings_dialog.py +522 -0
- statable_gui/common_widgets.py +537 -0
- statable_gui/condition_builder_dialog.py +500 -0
- statable_gui/condition_edit_dialog.py +123 -0
- statable_gui/condition_template_dialog.py +58 -0
- statable_gui/config.py +21 -0
- statable_gui/dialogs.py +439 -0
- statable_gui/event_definition_dialog.py +605 -0
- statable_gui/event_delivery_settings_dialog.py +191 -0
- statable_gui/event_queue_dialog.py +245 -0
- statable_gui/global_defs.py +167 -0
- statable_gui/global_defs_dialog.py +796 -0
- statable_gui/i18n/__init__.py +118 -0
- statable_gui/interrupt_handler_edit_dialog.py +810 -0
- statable_gui/layer_settings_dialog.py +180 -0
- statable_gui/libcntrl/__init__.py +23 -0
- statable_gui/libcntrl/condition_library.py +13 -0
- statable_gui/libcntrl/literal_library.py +13 -0
- statable_gui/libcntrl/literal_management_dialog.py +185 -0
- statable_gui/libcntrl/role_function_edit_dialog.py +145 -0
- statable_gui/libcntrl/role_function_library.py +13 -0
- statable_gui/literal_definition_dialog.py +62 -0
- statable_gui/logger.py +83 -0
- statable_gui/main_window.py +1380 -0
- statable_gui/matrix_table.py +413 -0
- statable_gui/preference_keys.py +18 -0
- statable_gui/preferences.py +74 -0
- statable_gui/role_function_dialog.py +244 -0
- statable_gui/sample_data.py +11 -0
- statable_gui/state_actions_dialog.py +398 -0
- statable_gui/symbol_picker.py +170 -0
- statable_gui/traceball.py +37 -0
- statable_gui/transition_editor_direct/__init__.py +15 -0
- statable_gui/transition_editor_direct/actions_tab.py +856 -0
- statable_gui/transition_editor_direct/code_widget.py +344 -0
- statable_gui/transition_editor_direct/coverage_analyzer.py +163 -0
- statable_gui/transition_editor_direct/dialog.py +335 -0
- statable_gui/transition_editor_direct/draft.py +270 -0
- statable_gui/transition_editor_direct/edit_dialogs.py +160 -0
- statable_gui/transition_editor_direct/overview_tab.py +173 -0
- statable_gui/transition_editor_direct/palette_widget.py +233 -0
- statable_gui/transition_editor_direct/relations_edit_dialog.py +156 -0
- statable_gui/transition_editor_direct/relations_tab.py +230 -0
- statable_gui/transition_editor_direct/system_global_dialog.py +52 -0
- statable_gui/transition_editor_direct/transition_actions_dialog.py +109 -0
- statable_gui/transition_editor_direct/transitions_tab.py +620 -0
- statable_gui/validation_dialog.py +285 -0
- statable_gui/widgets.py +1315 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# codegen/enum_generator.py
|
|
2
|
+
"""
|
|
3
|
+
C enum code generation module (multi-layer state machine support)
|
|
4
|
+
|
|
5
|
+
[v1.5 fix]
|
|
6
|
+
- generate_event_enum: filter out empty-name events from the main loop
|
|
7
|
+
(NONE is already handled at the top, preventing duplicate definitions)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
12
|
+
import logging
|
|
13
|
+
from string import Template
|
|
14
|
+
from typing import Dict, List, Any, Optional
|
|
15
|
+
|
|
16
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
17
|
+
|
|
18
|
+
from statable.model import State, Event, StateType, EventKind
|
|
19
|
+
from statable.global_defs import EventFlag
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from .naming_convention import CNamingConvention
|
|
23
|
+
from .code_templates import CodeTemplates
|
|
24
|
+
except ImportError:
|
|
25
|
+
from naming_convention import CNamingConvention
|
|
26
|
+
from code_templates import CodeTemplates
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CEnumGenerator:
|
|
32
|
+
"""C enum code generation class (multi-layer state machine support)"""
|
|
33
|
+
|
|
34
|
+
ENUM_TEMPLATES = {
|
|
35
|
+
'section_comment': Template(
|
|
36
|
+
'/* $description */\n'
|
|
37
|
+
),
|
|
38
|
+
'enum_start': 'typedef enum {\n',
|
|
39
|
+
'value_with_comment': Template(
|
|
40
|
+
' $name = $value, /* $comment */\n'
|
|
41
|
+
),
|
|
42
|
+
'value': Template(
|
|
43
|
+
' $name = $value,\n'
|
|
44
|
+
),
|
|
45
|
+
'max_value': Template(
|
|
46
|
+
' $name /* element count (for system use) */\n'
|
|
47
|
+
),
|
|
48
|
+
'enum_end': Template(
|
|
49
|
+
'} $type_name;\n'
|
|
50
|
+
),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
LAYER_TYPE_NAMES = {
|
|
54
|
+
'state': {
|
|
55
|
+
'value_prefix': 'STATE',
|
|
56
|
+
'type_suffix': '_t',
|
|
57
|
+
'max_suffix': '_MAX',
|
|
58
|
+
'none_value': None,
|
|
59
|
+
},
|
|
60
|
+
'event': {
|
|
61
|
+
'value_prefix': 'EVENT',
|
|
62
|
+
'type_suffix': '_t',
|
|
63
|
+
'max_suffix': '_MAX',
|
|
64
|
+
'none_value': 'NONE',
|
|
65
|
+
},
|
|
66
|
+
'flag': {
|
|
67
|
+
'value_prefix': 'FLAG',
|
|
68
|
+
'type_suffix': '_t',
|
|
69
|
+
'max_suffix': '_MAX',
|
|
70
|
+
'none_value': None,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
COMMENT_FORMATS = {
|
|
75
|
+
'state': {
|
|
76
|
+
'description_suffix': None,
|
|
77
|
+
'type_suffix': 'Type: {type_name}',
|
|
78
|
+
'title_prefix': 'Title: ',
|
|
79
|
+
},
|
|
80
|
+
'event': {
|
|
81
|
+
'description_suffix': None,
|
|
82
|
+
'kind_suffix': 'Kind: {kind_name}',
|
|
83
|
+
'title_prefix': 'Title: ',
|
|
84
|
+
},
|
|
85
|
+
'flag': {
|
|
86
|
+
'description_suffix': None,
|
|
87
|
+
'title_prefix': 'Title: ',
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
def __init__(self):
|
|
92
|
+
self.naming = CNamingConvention()
|
|
93
|
+
self.templates = CodeTemplates()
|
|
94
|
+
self.strings = self.templates.STRINGS
|
|
95
|
+
self.formats = self.templates.FORMATS
|
|
96
|
+
self.layer_name: str = ""
|
|
97
|
+
|
|
98
|
+
def set_layer(self, layer_name: str):
|
|
99
|
+
self.layer_name = layer_name
|
|
100
|
+
logger.debug(f"CEnumGenerator.set_layer: layer_name='{layer_name}'")
|
|
101
|
+
|
|
102
|
+
def _log_debug(self, message, level='debug'):
|
|
103
|
+
log_func = getattr(logger, level, logger.debug)
|
|
104
|
+
log_func(message)
|
|
105
|
+
|
|
106
|
+
def _state_value_name(self, state_name: str) -> str:
|
|
107
|
+
pascal = self.naming.to_pascal_case(state_name) if state_name else "Unknown"
|
|
108
|
+
if self.layer_name:
|
|
109
|
+
return f"STATE_{self.layer_name}_{pascal}"
|
|
110
|
+
return f"STATE_{pascal}"
|
|
111
|
+
|
|
112
|
+
def _event_value_name(self, event_name: str) -> str:
|
|
113
|
+
if not event_name:
|
|
114
|
+
upper = "NONE"
|
|
115
|
+
else:
|
|
116
|
+
upper = self.naming.to_upper_snake(event_name)
|
|
117
|
+
if self.layer_name:
|
|
118
|
+
return f"EVENT_{self.layer_name}_{upper}"
|
|
119
|
+
return f"EVENT_{upper}"
|
|
120
|
+
|
|
121
|
+
def _flag_value_name(self, flag_name: str) -> str:
|
|
122
|
+
upper = self.naming.to_upper_snake(flag_name)
|
|
123
|
+
return f"FLAG_{upper}"
|
|
124
|
+
|
|
125
|
+
def _state_type_name(self) -> str:
|
|
126
|
+
if self.layer_name:
|
|
127
|
+
return f"STATE_{self.layer_name}_t"
|
|
128
|
+
return "STATE_t"
|
|
129
|
+
|
|
130
|
+
def _event_type_name(self) -> str:
|
|
131
|
+
if self.layer_name:
|
|
132
|
+
return f"EVENT_{self.layer_name}_t"
|
|
133
|
+
return "EVENT_t"
|
|
134
|
+
|
|
135
|
+
def _flag_type_name(self) -> str:
|
|
136
|
+
return "FLAG_t"
|
|
137
|
+
|
|
138
|
+
def _state_max_name(self) -> str:
|
|
139
|
+
if self.layer_name:
|
|
140
|
+
return f"STATE_{self.layer_name}_MAX"
|
|
141
|
+
return "STATE_MAX"
|
|
142
|
+
|
|
143
|
+
def _event_max_name(self) -> str:
|
|
144
|
+
if self.layer_name:
|
|
145
|
+
return f"EVENT_{self.layer_name}_MAX"
|
|
146
|
+
return "EVENT_MAX"
|
|
147
|
+
|
|
148
|
+
def _flag_max_name(self) -> str:
|
|
149
|
+
return "FLAG_MAX"
|
|
150
|
+
|
|
151
|
+
def _generate_state_comment(self, state: State) -> str:
|
|
152
|
+
comments = []
|
|
153
|
+
if getattr(state, 'description', ''):
|
|
154
|
+
comments.append(state.description)
|
|
155
|
+
if getattr(state, 'type', None) and state.type != StateType.NORMAL:
|
|
156
|
+
comments.append(f"Type: {state.type.name}")
|
|
157
|
+
if getattr(state, 'title', ''):
|
|
158
|
+
comments.append(f"Title: {state.title}")
|
|
159
|
+
return ' '.join(comments)
|
|
160
|
+
|
|
161
|
+
def _generate_event_comment(self, event: Event) -> str:
|
|
162
|
+
comments = []
|
|
163
|
+
if getattr(event, 'description', ''):
|
|
164
|
+
comments.append(event.description)
|
|
165
|
+
if getattr(event, 'kind', None) and event.kind != EventKind.SIGNAL:
|
|
166
|
+
comments.append(f"Kind: {event.kind.name}")
|
|
167
|
+
if getattr(event, 'title', ''):
|
|
168
|
+
comments.append(f"Title: {event.title}")
|
|
169
|
+
return ' '.join(comments)
|
|
170
|
+
|
|
171
|
+
def _generate_flag_comment(self, flag: EventFlag) -> str:
|
|
172
|
+
comments = []
|
|
173
|
+
if getattr(flag, 'description', ''):
|
|
174
|
+
comments.append(flag.description)
|
|
175
|
+
if getattr(flag, 'title', ''):
|
|
176
|
+
comments.append(f"Title: {flag.title}")
|
|
177
|
+
return ' '.join(comments)
|
|
178
|
+
|
|
179
|
+
def _generate_enum_values(self, items: List[Any],
|
|
180
|
+
value_name_func,
|
|
181
|
+
comment_func,
|
|
182
|
+
none_value: Optional[str] = None,
|
|
183
|
+
none_comment: str = "") -> str:
|
|
184
|
+
T = self.ENUM_TEMPLATES
|
|
185
|
+
parts = []
|
|
186
|
+
|
|
187
|
+
if none_value is not None:
|
|
188
|
+
none_name = value_name_func("")
|
|
189
|
+
parts.append(T['value_with_comment'].substitute(
|
|
190
|
+
name=none_name,
|
|
191
|
+
value=0,
|
|
192
|
+
comment=none_comment,
|
|
193
|
+
))
|
|
194
|
+
start_index = 1
|
|
195
|
+
else:
|
|
196
|
+
start_index = 0
|
|
197
|
+
|
|
198
|
+
for i, item in enumerate(items):
|
|
199
|
+
value = start_index + i
|
|
200
|
+
name = value_name_func(getattr(item, 'name', 'unnamed'))
|
|
201
|
+
comment = comment_func(item)
|
|
202
|
+
|
|
203
|
+
if comment:
|
|
204
|
+
parts.append(T['value_with_comment'].substitute(
|
|
205
|
+
name=name, value=value, comment=comment,
|
|
206
|
+
))
|
|
207
|
+
else:
|
|
208
|
+
parts.append(T['value'].substitute(
|
|
209
|
+
name=name, value=value,
|
|
210
|
+
))
|
|
211
|
+
|
|
212
|
+
return ''.join(parts)
|
|
213
|
+
|
|
214
|
+
def generate_state_enum(self, states: List[State]) -> str:
|
|
215
|
+
if not states:
|
|
216
|
+
return ''
|
|
217
|
+
|
|
218
|
+
self._log_debug(f"=== generate_state_enum START: {len(states)} states ===")
|
|
219
|
+
T = self.ENUM_TEMPLATES
|
|
220
|
+
parts = []
|
|
221
|
+
|
|
222
|
+
desc = f"{self.layer_name} layer state definitions" if self.layer_name else "State definitions"
|
|
223
|
+
parts.append(T['section_comment'].substitute(description=desc))
|
|
224
|
+
|
|
225
|
+
parts.append(T['enum_start'])
|
|
226
|
+
|
|
227
|
+
parts.append(self._generate_enum_values(
|
|
228
|
+
items=states,
|
|
229
|
+
value_name_func=self._state_value_name,
|
|
230
|
+
comment_func=self._generate_state_comment,
|
|
231
|
+
none_value=None,
|
|
232
|
+
))
|
|
233
|
+
|
|
234
|
+
parts.append(T['max_value'].substitute(name=self._state_max_name()))
|
|
235
|
+
parts.append(T['enum_end'].substitute(type_name=self._state_type_name()))
|
|
236
|
+
|
|
237
|
+
result = ''.join(parts)
|
|
238
|
+
self._log_debug(f"=== generate_state_enum END ===")
|
|
239
|
+
return result
|
|
240
|
+
|
|
241
|
+
def generate_event_enum(self, events: List[Event]) -> str:
|
|
242
|
+
"""
|
|
243
|
+
Generate event enum (including NONE = 0).
|
|
244
|
+
|
|
245
|
+
[v1.5 fix]
|
|
246
|
+
Empty-name events (used for completion transitions, `name=""`)
|
|
247
|
+
are already handled as NONE at the top, so exclude them from
|
|
248
|
+
the main loop. This prevents duplicate EVENT_<Layer>_NONE.
|
|
249
|
+
"""
|
|
250
|
+
if not events:
|
|
251
|
+
return ''
|
|
252
|
+
|
|
253
|
+
# v1.5 fix: exclude empty-name events
|
|
254
|
+
non_empty_events = [
|
|
255
|
+
e for e in events if getattr(e, 'name', '')
|
|
256
|
+
]
|
|
257
|
+
filtered = len(events) - len(non_empty_events)
|
|
258
|
+
if filtered:
|
|
259
|
+
self._log_debug(
|
|
260
|
+
f"generate_event_enum: filtered out {filtered} empty-name "
|
|
261
|
+
f"event(s) (already processed as NONE)"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
self._log_debug(f"=== generate_event_enum START: "
|
|
265
|
+
f"{len(non_empty_events)} events ===")
|
|
266
|
+
T = self.ENUM_TEMPLATES
|
|
267
|
+
parts = []
|
|
268
|
+
|
|
269
|
+
desc = f"{self.layer_name} layer event definitions" if self.layer_name else "Event definitions"
|
|
270
|
+
parts.append(T['section_comment'].substitute(description=desc))
|
|
271
|
+
|
|
272
|
+
parts.append(T['enum_start'])
|
|
273
|
+
|
|
274
|
+
parts.append(self._generate_enum_values(
|
|
275
|
+
items=non_empty_events,
|
|
276
|
+
value_name_func=self._event_value_name,
|
|
277
|
+
comment_func=self._generate_event_comment,
|
|
278
|
+
none_value="NONE",
|
|
279
|
+
none_comment="completion transition",
|
|
280
|
+
))
|
|
281
|
+
|
|
282
|
+
parts.append(T['max_value'].substitute(name=self._event_max_name()))
|
|
283
|
+
parts.append(T['enum_end'].substitute(type_name=self._event_type_name()))
|
|
284
|
+
|
|
285
|
+
result = ''.join(parts)
|
|
286
|
+
self._log_debug(f"=== generate_event_enum END ===")
|
|
287
|
+
return result
|
|
288
|
+
|
|
289
|
+
def generate_flag_enum(self, flags: List[EventFlag]) -> str:
|
|
290
|
+
if not flags:
|
|
291
|
+
return ''
|
|
292
|
+
|
|
293
|
+
self._log_debug(f"=== generate_flag_enum START: {len(flags)} flags ===")
|
|
294
|
+
T = self.ENUM_TEMPLATES
|
|
295
|
+
parts = []
|
|
296
|
+
|
|
297
|
+
parts.append(T['section_comment'].substitute(description="Event flag definitions"))
|
|
298
|
+
parts.append(T['enum_start'])
|
|
299
|
+
|
|
300
|
+
parts.append(self._generate_enum_values(
|
|
301
|
+
items=flags,
|
|
302
|
+
value_name_func=self._flag_value_name,
|
|
303
|
+
comment_func=self._generate_flag_comment,
|
|
304
|
+
none_value=None,
|
|
305
|
+
))
|
|
306
|
+
|
|
307
|
+
parts.append(T['max_value'].substitute(name=self._flag_max_name()))
|
|
308
|
+
parts.append(T['enum_end'].substitute(type_name=self._flag_type_name()))
|
|
309
|
+
|
|
310
|
+
result = ''.join(parts)
|
|
311
|
+
self._log_debug(f"=== generate_flag_enum END ===")
|
|
312
|
+
return result
|
|
313
|
+
|
|
314
|
+
def generate_all_enums(self, states: List[State], events: List[Event],
|
|
315
|
+
flags: List[EventFlag] = None) -> str:
|
|
316
|
+
self._log_debug(f"=== generate_all_enums START: "
|
|
317
|
+
f"{len(states)} states, {len(events)} events, "
|
|
318
|
+
f"{len(flags) if flags else 0} flags ===")
|
|
319
|
+
parts = []
|
|
320
|
+
|
|
321
|
+
state_enum = self.generate_state_enum(states)
|
|
322
|
+
if state_enum:
|
|
323
|
+
parts.append(state_enum)
|
|
324
|
+
parts.append("")
|
|
325
|
+
|
|
326
|
+
event_enum = self.generate_event_enum(events)
|
|
327
|
+
if event_enum:
|
|
328
|
+
parts.append(event_enum)
|
|
329
|
+
parts.append("")
|
|
330
|
+
|
|
331
|
+
if flags:
|
|
332
|
+
flag_enum = self.generate_flag_enum(flags)
|
|
333
|
+
if flag_enum:
|
|
334
|
+
parts.append(flag_enum)
|
|
335
|
+
|
|
336
|
+
result = '\n'.join(parts)
|
|
337
|
+
self._log_debug(f"=== generate_all_enums END: {len(result)} chars ===")
|
|
338
|
+
return result
|
|
339
|
+
|
|
340
|
+
def generate_bit_mask_enum(self, flags: List[EventFlag]) -> str:
|
|
341
|
+
if not flags:
|
|
342
|
+
return ''
|
|
343
|
+
|
|
344
|
+
self._log_debug(f"=== generate_bit_mask_enum START: {len(flags)} flags ===")
|
|
345
|
+
T = self.ENUM_TEMPLATES
|
|
346
|
+
parts = []
|
|
347
|
+
|
|
348
|
+
parts.append('/* Event flag bitmask definitions */\n')
|
|
349
|
+
parts.append('/* Used when managing flags as individual bits */\n')
|
|
350
|
+
parts.append(T['enum_start'])
|
|
351
|
+
|
|
352
|
+
for i, flag in enumerate(flags):
|
|
353
|
+
bit_value = 1 << i
|
|
354
|
+
upper = self.naming.to_upper_snake(getattr(flag, 'name', 'unnamed'))
|
|
355
|
+
name = f"FLAG_MASK_{upper}"
|
|
356
|
+
comment = self._generate_flag_comment(flag)
|
|
357
|
+
if comment:
|
|
358
|
+
parts.append(f" {name} = 0x{bit_value:02X}, /* {comment} */\n")
|
|
359
|
+
else:
|
|
360
|
+
parts.append(f" {name} = 0x{bit_value:02X},\n")
|
|
361
|
+
|
|
362
|
+
parts.append(T['enum_end'].substitute(type_name="FLAG_MASK_t"))
|
|
363
|
+
|
|
364
|
+
result = ''.join(parts)
|
|
365
|
+
self._log_debug(f"=== generate_bit_mask_enum END ===")
|
|
366
|
+
return result
|
|
367
|
+
|
|
368
|
+
def generate_enum(self, enum_type: str, items: List[Any]) -> str:
|
|
369
|
+
if enum_type == 'state':
|
|
370
|
+
return self.generate_state_enum(items)
|
|
371
|
+
elif enum_type == 'event':
|
|
372
|
+
return self.generate_event_enum(items)
|
|
373
|
+
elif enum_type == 'flag':
|
|
374
|
+
return self.generate_flag_enum(items)
|
|
375
|
+
else:
|
|
376
|
+
logger.warning(f"Unknown enum_type: {enum_type}")
|
|
377
|
+
return ""
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# codegen/event_queue_generator.py
|
|
2
|
+
"""\nEvent queue code generation module (fully data-driven)\n"""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Dict, Callable, List, Any
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
10
|
+
|
|
11
|
+
from statable.global_defs import EventQueueDef, GlobalDefinitions
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from .type_mapper import CTypeMapper
|
|
15
|
+
from .naming_convention import CNamingConvention
|
|
16
|
+
from .code_templates import CodeTemplates
|
|
17
|
+
except ImportError:
|
|
18
|
+
from type_mapper import CTypeMapper
|
|
19
|
+
from naming_convention import CNamingConvention
|
|
20
|
+
from code_templates import CodeTemplates
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EventQueueGenerator:
|
|
26
|
+
"""Event queue code generation class (fully data-driven)"""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.mapper = CTypeMapper()
|
|
30
|
+
self.naming = CNamingConvention()
|
|
31
|
+
self.templates = CodeTemplates()
|
|
32
|
+
self.strings = self.templates.STRINGS
|
|
33
|
+
self.formats = self.templates.FORMATS
|
|
34
|
+
|
|
35
|
+
# Generation step definition
|
|
36
|
+
self.struct_steps = [
|
|
37
|
+
{'action': 'comment'},
|
|
38
|
+
{'action': 'struct_start'},
|
|
39
|
+
{'action': 'members'},
|
|
40
|
+
{'action': 'struct_end'},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
self.enqueue_steps = [
|
|
44
|
+
{'action': 'comment'},
|
|
45
|
+
{'action': 'signature'},
|
|
46
|
+
{'action': 'open'},
|
|
47
|
+
{'action': 'null_check'},
|
|
48
|
+
{'action': 'full_check'},
|
|
49
|
+
{'action': 'enqueue_body'},
|
|
50
|
+
{'action': 'close'},
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
self.dequeue_steps = [
|
|
54
|
+
{'action': 'comment'},
|
|
55
|
+
{'action': 'signature'},
|
|
56
|
+
{'action': 'open'},
|
|
57
|
+
{'action': 'null_check'},
|
|
58
|
+
{'action': 'empty_check'},
|
|
59
|
+
{'action': 'dequeue_body'},
|
|
60
|
+
{'action': 'close'},
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# Step execution dictionary
|
|
64
|
+
self.struct_executors: Dict[str, Callable] = {
|
|
65
|
+
'comment': self._execute_struct_comment,
|
|
66
|
+
'struct_start': self._execute_struct_start,
|
|
67
|
+
'members': self._execute_members,
|
|
68
|
+
'struct_end': self._execute_struct_end,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
self.enqueue_executors: Dict[str, Callable] = {
|
|
72
|
+
'comment': self._execute_enqueue_comment,
|
|
73
|
+
'signature': self._execute_enqueue_signature,
|
|
74
|
+
'open': self._execute_open,
|
|
75
|
+
'null_check': self._execute_null_check,
|
|
76
|
+
'full_check': self._execute_full_check,
|
|
77
|
+
'enqueue_body': self._execute_enqueue_body,
|
|
78
|
+
'close': self._execute_close,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
self.dequeue_executors: Dict[str, Callable] = {
|
|
82
|
+
'comment': self._execute_dequeue_comment,
|
|
83
|
+
'signature': self._execute_dequeue_signature,
|
|
84
|
+
'open': self._execute_open,
|
|
85
|
+
'null_check': self._execute_null_check,
|
|
86
|
+
'empty_check': self._execute_empty_check,
|
|
87
|
+
'dequeue_body': self._execute_dequeue_body,
|
|
88
|
+
'close': self._execute_close,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
def _log_debug(self, message, level='debug'):
|
|
92
|
+
log_func = getattr(logger, level, logger.debug)
|
|
93
|
+
log_func(message)
|
|
94
|
+
|
|
95
|
+
# ===== Name generation =====
|
|
96
|
+
def _generate_struct_name(self, queue):
|
|
97
|
+
return self.naming.create_type_name(getattr(queue, 'name', 'EventQueue'))
|
|
98
|
+
|
|
99
|
+
def _generate_func_prefix(self, queue):
|
|
100
|
+
return f"EventQueue_{self.naming.to_pascal_case(getattr(queue, 'name', ''))}"
|
|
101
|
+
|
|
102
|
+
# ===== Struct generation step =====
|
|
103
|
+
def _execute_struct_comment(self, step, context):
|
|
104
|
+
queue = context.get('queue')
|
|
105
|
+
name = getattr(queue, 'name', 'unknown')
|
|
106
|
+
desc = getattr(queue, 'description', '')
|
|
107
|
+
lines = [f"/* Event queue: {name} */"]
|
|
108
|
+
if desc:
|
|
109
|
+
lines.append(f"/* {desc} */")
|
|
110
|
+
return lines
|
|
111
|
+
|
|
112
|
+
def _execute_struct_start(self, step, context):
|
|
113
|
+
return ["typedef struct {"]
|
|
114
|
+
|
|
115
|
+
def _execute_members(self, step, context):
|
|
116
|
+
queue = context.get('queue')
|
|
117
|
+
indent = self.strings['indent_1']
|
|
118
|
+
size = getattr(queue, 'size', 0)
|
|
119
|
+
element_type = self.mapper.map_type(getattr(queue, 'element_type', 'EVENT_t'))
|
|
120
|
+
|
|
121
|
+
lines = []
|
|
122
|
+
lines.append(f"{indent}{element_type} buffer[{size}];")
|
|
123
|
+
lines.append(f"{indent}uint8_t head;")
|
|
124
|
+
lines.append(f"{indent}uint8_t tail;")
|
|
125
|
+
lines.append(f"{indent}uint8_t count;")
|
|
126
|
+
|
|
127
|
+
if getattr(queue, 'interrupt_safe', False):
|
|
128
|
+
lines.append(f"{indent}bool interrupt_safe;")
|
|
129
|
+
|
|
130
|
+
if getattr(queue, 'rtos_enabled', False):
|
|
131
|
+
lines.append(f"{indent}bool rtos_enabled;")
|
|
132
|
+
|
|
133
|
+
return lines
|
|
134
|
+
|
|
135
|
+
def _execute_struct_end(self, step, context):
|
|
136
|
+
queue = context.get('queue')
|
|
137
|
+
type_name = self._generate_struct_name(queue)
|
|
138
|
+
return [f"}} {type_name};"]
|
|
139
|
+
|
|
140
|
+
# ===== Enqueue generation step =====
|
|
141
|
+
def _execute_enqueue_comment(self, step, context):
|
|
142
|
+
queue = context.get('queue')
|
|
143
|
+
name = getattr(queue, 'name', 'unknown')
|
|
144
|
+
return [
|
|
145
|
+
"/**",
|
|
146
|
+
f" * @brief Enqueue event ({name})",
|
|
147
|
+
" * @param queue Queue",
|
|
148
|
+
" * @param event Event",
|
|
149
|
+
" * @return success: true, failure: false",
|
|
150
|
+
" */",
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
def _execute_enqueue_signature(self, step, context):
|
|
154
|
+
queue = context.get('queue')
|
|
155
|
+
type_name = self._generate_struct_name(queue)
|
|
156
|
+
func_name = self._generate_func_prefix(queue) + "_Enqueue"
|
|
157
|
+
return [f"bool {func_name}({type_name} *queue, EVENT_t event)"]
|
|
158
|
+
|
|
159
|
+
def _execute_dequeue_comment(self, step, context):
|
|
160
|
+
queue = context.get('queue')
|
|
161
|
+
name = getattr(queue, 'name', 'unknown')
|
|
162
|
+
return [
|
|
163
|
+
"/**",
|
|
164
|
+
f" * @brief Dequeue event ({name})",
|
|
165
|
+
" * @param queue Queue",
|
|
166
|
+
" * @param event Dequeued event",
|
|
167
|
+
" * @return success: true, failure: false",
|
|
168
|
+
" */",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
def _execute_dequeue_signature(self, step, context):
|
|
172
|
+
queue = context.get('queue')
|
|
173
|
+
type_name = self._generate_struct_name(queue)
|
|
174
|
+
func_name = self._generate_func_prefix(queue) + "_Dequeue"
|
|
175
|
+
return [f"bool {func_name}({type_name} *queue, EVENT_t *event)"]
|
|
176
|
+
|
|
177
|
+
def _execute_open(self, step, context):
|
|
178
|
+
return ["{"]
|
|
179
|
+
|
|
180
|
+
def _execute_close(self, step, context):
|
|
181
|
+
return ["}"]
|
|
182
|
+
|
|
183
|
+
def _execute_null_check(self, step, context):
|
|
184
|
+
indent = self.strings['indent_1']
|
|
185
|
+
lines = []
|
|
186
|
+
lines.append(f"{indent}if (queue == NULL) {{")
|
|
187
|
+
lines.append(f"{indent}{indent}return false;")
|
|
188
|
+
lines.append(f"{indent}}}")
|
|
189
|
+
return lines
|
|
190
|
+
|
|
191
|
+
def _execute_full_check(self, step, context):
|
|
192
|
+
queue = context.get('queue')
|
|
193
|
+
size = getattr(queue, 'size', 0)
|
|
194
|
+
indent = self.strings['indent_1']
|
|
195
|
+
lines = []
|
|
196
|
+
lines.append(f"{indent}if (queue->count >= {size}) {{")
|
|
197
|
+
lines.append(f"{indent}{indent}return false; /* Queue full */")
|
|
198
|
+
lines.append(f"{indent}}}")
|
|
199
|
+
return lines
|
|
200
|
+
|
|
201
|
+
def _execute_empty_check(self, step, context):
|
|
202
|
+
indent = self.strings['indent_1']
|
|
203
|
+
lines = []
|
|
204
|
+
lines.append(f"{indent}if (queue->count == 0) {{")
|
|
205
|
+
lines.append(f"{indent}{indent}return false; /* Queue empty */")
|
|
206
|
+
lines.append(f"{indent}}}")
|
|
207
|
+
return lines
|
|
208
|
+
|
|
209
|
+
def _execute_enqueue_body(self, step, context):
|
|
210
|
+
queue = context.get('queue')
|
|
211
|
+
size = getattr(queue, 'size', 0)
|
|
212
|
+
indent = self.strings['indent_1']
|
|
213
|
+
lines = []
|
|
214
|
+
lines.append(f"{indent}queue->buffer[queue->tail] = event;")
|
|
215
|
+
lines.append(f"{indent}queue->tail = (queue->tail + 1) % {size};")
|
|
216
|
+
lines.append(f"{indent}queue->count++;")
|
|
217
|
+
lines.append(f"{indent}return true;")
|
|
218
|
+
return lines
|
|
219
|
+
|
|
220
|
+
def _execute_dequeue_body(self, step, context):
|
|
221
|
+
queue = context.get('queue')
|
|
222
|
+
size = getattr(queue, 'size', 0)
|
|
223
|
+
indent = self.strings['indent_1']
|
|
224
|
+
lines = []
|
|
225
|
+
lines.append(f"{indent}if (event == NULL) {{")
|
|
226
|
+
lines.append(f"{indent}{indent}return false;")
|
|
227
|
+
lines.append(f"{indent}}}")
|
|
228
|
+
lines.append(f"{indent}*event = queue->buffer[queue->head];")
|
|
229
|
+
lines.append(f"{indent}queue->head = (queue->head + 1) % {size};")
|
|
230
|
+
lines.append(f"{indent}queue->count--;")
|
|
231
|
+
lines.append(f"{indent}return true;")
|
|
232
|
+
return lines
|
|
233
|
+
|
|
234
|
+
# ===== Public methods =====
|
|
235
|
+
def generate_struct(self, queue: EventQueueDef) -> str:
|
|
236
|
+
"""Event queue struct generation"""
|
|
237
|
+
self._log_debug(f"Generating struct for: {getattr(queue, 'name', 'unknown')}")
|
|
238
|
+
context = {'queue': queue}
|
|
239
|
+
lines = []
|
|
240
|
+
for step in self.struct_steps:
|
|
241
|
+
executor = self.struct_executors.get(step['action'])
|
|
242
|
+
if executor:
|
|
243
|
+
lines.extend(executor(step, context))
|
|
244
|
+
return '\n'.join(lines)
|
|
245
|
+
|
|
246
|
+
def generate_enqueue_function(self, queue: EventQueueDef) -> str:
|
|
247
|
+
"""Enqueue function generation"""
|
|
248
|
+
self._log_debug(f"Generating enqueue for: {getattr(queue, 'name', 'unknown')}")
|
|
249
|
+
context = {'queue': queue}
|
|
250
|
+
lines = []
|
|
251
|
+
for step in self.enqueue_steps:
|
|
252
|
+
executor = self.enqueue_executors.get(step['action'])
|
|
253
|
+
if executor:
|
|
254
|
+
lines.extend(executor(step, context))
|
|
255
|
+
return '\n'.join(lines)
|
|
256
|
+
|
|
257
|
+
def generate_dequeue_function(self, queue: EventQueueDef) -> str:
|
|
258
|
+
"""Dequeue function generation"""
|
|
259
|
+
self._log_debug(f"Generating dequeue for: {getattr(queue, 'name', 'unknown')}")
|
|
260
|
+
context = {'queue': queue}
|
|
261
|
+
lines = []
|
|
262
|
+
for step in self.dequeue_steps:
|
|
263
|
+
executor = self.dequeue_executors.get(step['action'])
|
|
264
|
+
if executor:
|
|
265
|
+
lines.extend(executor(step, context))
|
|
266
|
+
return '\n'.join(lines)
|
|
267
|
+
|
|
268
|
+
def generate_all_code(self, queue: EventQueueDef) -> str:
|
|
269
|
+
"""Generate all event queue code"""
|
|
270
|
+
self._log_debug(f"Generating all code for: {getattr(queue, 'name', 'unknown')}")
|
|
271
|
+
lines = []
|
|
272
|
+
lines.append(self.generate_struct(queue))
|
|
273
|
+
lines.append("")
|
|
274
|
+
lines.append(self.generate_enqueue_function(queue))
|
|
275
|
+
lines.append("")
|
|
276
|
+
lines.append(self.generate_dequeue_function(queue))
|
|
277
|
+
return '\n'.join(lines)
|
|
278
|
+
|
|
279
|
+
def generate_all_structs(self, global_defs: GlobalDefinitions) -> str:
|
|
280
|
+
"""Generate all event queue structs"""
|
|
281
|
+
lines = []
|
|
282
|
+
for queue in getattr(global_defs, 'event_queues', []):
|
|
283
|
+
lines.append(self.generate_struct(queue))
|
|
284
|
+
lines.append("")
|
|
285
|
+
return '\n'.join(lines)
|
|
286
|
+
|
|
287
|
+
def generate_all_functions(self, global_defs: GlobalDefinitions) -> str:
|
|
288
|
+
"""Generate all event queue functions"""
|
|
289
|
+
lines = []
|
|
290
|
+
for queue in getattr(global_defs, 'event_queues', []):
|
|
291
|
+
lines.append(self.generate_enqueue_function(queue))
|
|
292
|
+
lines.append("")
|
|
293
|
+
lines.append(self.generate_dequeue_function(queue))
|
|
294
|
+
lines.append("")
|
|
295
|
+
return '\n'.join(lines)
|
|
296
|
+
|
|
297
|
+
def generate_all(self, global_defs: GlobalDefinitions) -> str:
|
|
298
|
+
"""Generate all event queue code"""
|
|
299
|
+
lines = []
|
|
300
|
+
for queue in getattr(global_defs, 'event_queues', []):
|
|
301
|
+
lines.append(self.generate_all_code(queue))
|
|
302
|
+
lines.append("")
|
|
303
|
+
return '\n'.join(lines)
|