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.
Files changed (117) hide show
  1. codegen/__init__.py +38 -0
  2. codegen/c_code_generator.py +1752 -0
  3. codegen/code_merger.py +371 -0
  4. codegen/code_templates.py +509 -0
  5. codegen/config.py +167 -0
  6. codegen/enum_generator.py +377 -0
  7. codegen/event_queue_generator.py +303 -0
  8. codegen/interrupt_generator.py +345 -0
  9. codegen/naming_convention.py +171 -0
  10. codegen/osal_generator.py +282 -0
  11. codegen/role_function_generator.py +1244 -0
  12. codegen/sample_data.py +269 -0
  13. codegen/state_actions_generator.py +395 -0
  14. codegen/struct_generator.py +721 -0
  15. codegen/timer_generator.py +299 -0
  16. codegen/transition_generator.py +907 -0
  17. codegen/type_mapper.py +69 -0
  18. codegen/validate/__init__.py +38 -0
  19. codegen/validate/change_actions.py +90 -0
  20. codegen/validate/change_applier.py +416 -0
  21. codegen/validate/clipboard_manager.py +47 -0
  22. codegen/validate/data/__init__.py +30 -0
  23. codegen/validate/data/action_definitions.py +200 -0
  24. codegen/validate/data/keywords.py +46 -0
  25. codegen/validate/data/prompt_templates.py +176 -0
  26. codegen/validate/data/validation_rules.py +205 -0
  27. codegen/validate/items/__init__.py +30 -0
  28. codegen/validate/items/base_validator.py +25 -0
  29. codegen/validate/items/cell_validator.py +275 -0
  30. codegen/validate/items/custom_type_validator.py +75 -0
  31. codegen/validate/items/event_validator.py +78 -0
  32. codegen/validate/items/flag_validator.py +74 -0
  33. codegen/validate/items/interrupt_validator.py +75 -0
  34. codegen/validate/items/queue_validator.py +73 -0
  35. codegen/validate/items/role_function_validator.py +175 -0
  36. codegen/validate/items/state_validator.py +88 -0
  37. codegen/validate/items/timer_validator.py +120 -0
  38. codegen/validate/items/transition_validator.py +113 -0
  39. codegen/validate/items/variable_validator.py +132 -0
  40. codegen/validate/logger.py +52 -0
  41. codegen/validate/models.py +177 -0
  42. codegen/validate/prompt_generator.py +724 -0
  43. codegen/validate/response_parser.py +154 -0
  44. codegen/validate/response_validator.py +275 -0
  45. codegen/validate/validation_dialog.py +322 -0
  46. codegen/validate/validator.py +94 -0
  47. codegen/variable_generator.py +419 -0
  48. statable/__init__.py +71 -0
  49. statable/__main__.py +49 -0
  50. statable/cli.py +251 -0
  51. statable/global_defs.py +256 -0
  52. statable/mermaid_gen.py +144 -0
  53. statable/model.py +426 -0
  54. statable/parser.py +64 -0
  55. statable/sample_data.py +435 -0
  56. statable/shared/__init__.py +24 -0
  57. statable/shared/condition_library.py +63 -0
  58. statable/shared/literal_library.py +66 -0
  59. statable/shared/role_function_library.py +150 -0
  60. statable/state_machine.py +121 -0
  61. statable/xml_io.py +1164 -0
  62. statable-3.1.0.dist-info/METADATA +151 -0
  63. statable-3.1.0.dist-info/RECORD +117 -0
  64. statable-3.1.0.dist-info/WHEEL +5 -0
  65. statable-3.1.0.dist-info/entry_points.txt +2 -0
  66. statable-3.1.0.dist-info/top_level.txt +3 -0
  67. statable_gui/__init__.py +23 -0
  68. statable_gui/action_edit_dialog.py +214 -0
  69. statable_gui/code_generation_dialog.py +486 -0
  70. statable_gui/code_generation_settings_dialog.py +522 -0
  71. statable_gui/common_widgets.py +537 -0
  72. statable_gui/condition_builder_dialog.py +500 -0
  73. statable_gui/condition_edit_dialog.py +123 -0
  74. statable_gui/condition_template_dialog.py +58 -0
  75. statable_gui/config.py +21 -0
  76. statable_gui/dialogs.py +439 -0
  77. statable_gui/event_definition_dialog.py +605 -0
  78. statable_gui/event_delivery_settings_dialog.py +191 -0
  79. statable_gui/event_queue_dialog.py +245 -0
  80. statable_gui/global_defs.py +167 -0
  81. statable_gui/global_defs_dialog.py +796 -0
  82. statable_gui/i18n/__init__.py +118 -0
  83. statable_gui/interrupt_handler_edit_dialog.py +810 -0
  84. statable_gui/layer_settings_dialog.py +180 -0
  85. statable_gui/libcntrl/__init__.py +23 -0
  86. statable_gui/libcntrl/condition_library.py +13 -0
  87. statable_gui/libcntrl/literal_library.py +13 -0
  88. statable_gui/libcntrl/literal_management_dialog.py +185 -0
  89. statable_gui/libcntrl/role_function_edit_dialog.py +145 -0
  90. statable_gui/libcntrl/role_function_library.py +13 -0
  91. statable_gui/literal_definition_dialog.py +62 -0
  92. statable_gui/logger.py +83 -0
  93. statable_gui/main_window.py +1380 -0
  94. statable_gui/matrix_table.py +413 -0
  95. statable_gui/preference_keys.py +18 -0
  96. statable_gui/preferences.py +74 -0
  97. statable_gui/role_function_dialog.py +244 -0
  98. statable_gui/sample_data.py +11 -0
  99. statable_gui/state_actions_dialog.py +398 -0
  100. statable_gui/symbol_picker.py +170 -0
  101. statable_gui/traceball.py +37 -0
  102. statable_gui/transition_editor_direct/__init__.py +15 -0
  103. statable_gui/transition_editor_direct/actions_tab.py +856 -0
  104. statable_gui/transition_editor_direct/code_widget.py +344 -0
  105. statable_gui/transition_editor_direct/coverage_analyzer.py +163 -0
  106. statable_gui/transition_editor_direct/dialog.py +335 -0
  107. statable_gui/transition_editor_direct/draft.py +270 -0
  108. statable_gui/transition_editor_direct/edit_dialogs.py +160 -0
  109. statable_gui/transition_editor_direct/overview_tab.py +173 -0
  110. statable_gui/transition_editor_direct/palette_widget.py +233 -0
  111. statable_gui/transition_editor_direct/relations_edit_dialog.py +156 -0
  112. statable_gui/transition_editor_direct/relations_tab.py +230 -0
  113. statable_gui/transition_editor_direct/system_global_dialog.py +52 -0
  114. statable_gui/transition_editor_direct/transition_actions_dialog.py +109 -0
  115. statable_gui/transition_editor_direct/transitions_tab.py +620 -0
  116. statable_gui/validation_dialog.py +285 -0
  117. statable_gui/widgets.py +1315 -0
@@ -0,0 +1,1752 @@
1
+ # codegen/c_code_generator.py
2
+ """
3
+ C code generation main class
4
+ (step-table driven / 13-file support / multi-layer support / by_layer / ISR)
5
+
6
+ Version: 2.2.9 (2026-09-20 / MISRA 17.3 fix)
7
+ - Fix (MISRA C:2012 Rule 17.3): statable_transitions_<Layer>.c now
8
+ directly includes statable_types_common.h so that LOG_* macros
9
+ (LOG_ERROR / LOG_DEBUG / ...) are visible in the file scope.
10
+ Previously they were reachable only via a deep include chain
11
+ (transitions.h -> types.h -> types_common.h); cppcheck did not
12
+ follow the chain and treated LOG_ERROR as an implicit declaration
13
+ (3 hits, one per layer).
14
+
15
+ Version: 2.2.8 (2026-09-20 / get_next_event emission)
16
+ Version: 2.2.7 (2026-09-20 / MISRA 17.3 LOG macros + GetNextEvent prototypes)
17
+ Version: 2.2.6 (2026-09-20 / MISRA 17.3 cross-layer include)
18
+ Version: 2.2.5 (2026-09-19)
19
+ Version: 2.1 (2026-09-13 / Stage 3: ISR context support)
20
+ Version: 1.6 (by_layer suffix / common types)
21
+ """
22
+
23
+ import sys
24
+ import os
25
+ import logging
26
+ from typing import Dict, Callable, List, Any, Optional, Tuple
27
+ from datetime import datetime
28
+
29
+ sys.path.append(
30
+ os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
31
+ )
32
+
33
+ from statable.state_machine import StateMachine
34
+ from statable.global_defs import GlobalDefinitions
35
+
36
+ try:
37
+ from .type_mapper import CTypeMapper
38
+ from .naming_convention import CNamingConvention
39
+ from .struct_generator import CStructGenerator
40
+ from .enum_generator import CEnumGenerator
41
+ from .transition_generator import TransitionGenerator
42
+ from .role_function_generator import RoleFunctionGenerator
43
+ from .variable_generator import VariableGenerator
44
+ from .event_queue_generator import EventQueueGenerator
45
+ from .interrupt_generator import InterruptGenerator
46
+ from .timer_generator import TimerGenerator
47
+ from .osal_generator import OSALGenerator
48
+ from .state_actions_generator import StateActionsGenerator
49
+ from .code_templates import CodeTemplates
50
+ from .code_merger import CodeMerger
51
+ from .config import CodeGenerationConfig, ConfigManager
52
+ except ImportError:
53
+ from type_mapper import CTypeMapper
54
+ from naming_convention import CNamingConvention
55
+ from struct_generator import CStructGenerator
56
+ from enum_generator import CEnumGenerator
57
+ from transition_generator import TransitionGenerator
58
+ from role_function_generator import RoleFunctionGenerator
59
+ from variable_generator import VariableGenerator
60
+ from event_queue_generator import EventQueueGenerator
61
+ from interrupt_generator import InterruptGenerator
62
+ from timer_generator import TimerGenerator
63
+ from osal_generator import OSALGenerator
64
+ from code_templates import CodeTemplates
65
+ from code_merger import CodeMerger
66
+ from config import CodeGenerationConfig, ConfigManager
67
+
68
+ logger = logging.getLogger(__name__)
69
+
70
+
71
+ class CCodeGenerator:
72
+ """C code generation main class
73
+ (step-table driven / 13-file support / multi-layer support)"""
74
+
75
+ # ================================================================
76
+ # [Table 1] Per-file step definitions
77
+ # ================================================================
78
+ FILE_STEPS: Dict[str, List[Dict[str, Any]]] = {
79
+ 'statable_types_common.h': [
80
+ {'action': 'file_header',
81
+ 'filename': 'statable_types_common.h'},
82
+ {'action': 'blank'},
83
+ {'action': 'guard_start'},
84
+ {'action': 'include_section', 'key': 'types'},
85
+ {'action': 'section_header', 'key': 'type_defs'},
86
+ {'action': 'blank'},
87
+ {'action': 'enums_common'},
88
+ {'action': 'blank'},
89
+ {'action': 'custom_types',
90
+ 'when': lambda c: bool(c['global_defs'].custom_types)},
91
+ # [C-52] EventQueueState_t must be declared BEFORE
92
+ # SystemContext_t, which embeds it by value.
93
+ {'action': 'struct', 'kind': 'layer_queue_types'},
94
+ {'action': 'blank'},
95
+ {'action': 'section_header', 'key': 'system_structs'},
96
+ {'action': 'blank'},
97
+ {'action': 'struct', 'kind': 'system_data'},
98
+ {'action': 'blank'},
99
+ {'action': 'struct', 'kind': 'event_flags'},
100
+ {'action': 'blank'},
101
+ {'action': 'struct', 'kind': 'system_context'},
102
+ {'action': 'blank'},
103
+ {'action': 'struct', 'kind': 'common_transition_context'},
104
+ {'action': 'blank'},
105
+ {'action': 'struct', 'kind': 'pending_event_macros'},
106
+ {'action': 'blank'},
107
+ # [C-52] per-layer queue macros (FIRE_EVENT_QUEUE_<Layer>)
108
+ {'action': 'struct', 'kind': 'layer_queue_macros'},
109
+ {'action': 'blank'},
110
+ {'action': 'section_header', 'key': 'var_macros'},
111
+ {'action': 'blank'},
112
+ {'action': 'var_macros'},
113
+ {'action': 'blank'},
114
+ {'action': 'section_header', 'key': 'common_function_decls'},
115
+ {'action': 'blank'},
116
+ {'action': 'common_function_decls'},
117
+ {'action': 'blank'},
118
+ {'action': 'guard_end'},
119
+ ],
120
+ 'statable_types.h': [
121
+ {'action': 'file_header',
122
+ 'filename': 'statable_types.h'},
123
+ {'action': 'blank'},
124
+ {'action': 'guard_start'},
125
+ {'action': 'include_section', 'key': 'types_layer'},
126
+ {'action': 'section_header', 'key': 'type_defs'},
127
+ {'action': 'blank'},
128
+ {'action': 'enums'},
129
+ {'action': 'blank'},
130
+ {'action': 'layer_transition_context'},
131
+ {'action': 'blank'},
132
+ {'action': 'guard_end'},
133
+ ],
134
+ 'statable_transitions.h': [
135
+ {'action': 'file_header',
136
+ 'filename': 'statable_transitions.h'},
137
+ {'action': 'blank'},
138
+ {'action': 'guard_start'},
139
+ {'action': 'include_section',
140
+ 'key': 'transitions_h'},
141
+ {'action': 'section_header',
142
+ 'key': 'function_decls'},
143
+ {'action': 'blank'},
144
+ {'action': 'state_machine_decl'},
145
+ {'action': 'blank'},
146
+ {'action': 'guard_end'},
147
+ ],
148
+ 'statable_transitions.c': [
149
+ {'action': 'file_header',
150
+ 'filename': 'statable_transitions.c'},
151
+ {'action': 'blank'},
152
+ {'action': 'include_section',
153
+ 'key': 'transitions_c'},
154
+ {'action': 'section_header',
155
+ 'key': 'transition_table'},
156
+ {'action': 'blank'},
157
+ {'action': 'cell_prototypes'},
158
+ {'action': 'blank'},
159
+ {'action': 'transition_table'},
160
+ {'action': 'blank'},
161
+ {'action': 'cell_functions'},
162
+ {'action': 'blank'},
163
+ {'action': 'section_header',
164
+ 'key': 'transition_func'},
165
+ {'action': 'blank'},
166
+ {'action': 'process_func'},
167
+ {'action': 'blank'},
168
+ {'action': 'section_header',
169
+ 'key': 'get_next_event'},
170
+ {'action': 'blank'},
171
+ {'action': 'get_next_event'},
172
+ ],
173
+ 'statable_role_functions.h': [
174
+ {'action': 'file_header',
175
+ 'filename': 'statable_role_functions.h'},
176
+ {'action': 'blank'},
177
+ {'action': 'guard_start'},
178
+ {'action': 'include_section',
179
+ 'key': 'role_functions_h'},
180
+ {'action': 'section_header',
181
+ 'key': 'role_functions'},
182
+ {'action': 'blank'},
183
+ {'action': 'role_decls'},
184
+ {'action': 'blank'},
185
+ {'action': 'guard_end'},
186
+ ],
187
+ 'statable_role_functions.c': [
188
+ {'action': 'file_header',
189
+ 'filename': 'statable_role_functions.c'},
190
+ {'action': 'blank'},
191
+ {'action': 'include_section',
192
+ 'key': 'role_functions_c'},
193
+ {'action': 'section_header', 'key': 'role_impl'},
194
+ {'action': 'blank'},
195
+ {'action': 'role_impls'},
196
+ ],
197
+ 'statable_state_actions.h': [
198
+ {'action': 'file_header',
199
+ 'filename': 'statable_state_actions.h'},
200
+ {'action': 'blank'},
201
+ {'action': 'guard_start'},
202
+ {'action': 'include_section', 'key': 'state_actions_h'},
203
+ {'action': 'section_header', 'key': 'state_actions'},
204
+ {'action': 'blank'},
205
+ {'action': 'state_actions_decls'},
206
+ {'action': 'blank'},
207
+ {'action': 'guard_end'},
208
+ ],
209
+ 'statable_state_actions.c': [
210
+ {'action': 'file_header',
211
+ 'filename': 'statable_state_actions.c'},
212
+ {'action': 'blank'},
213
+ {'action': 'include_section', 'key': 'state_actions_c'},
214
+ {'action': 'section_header', 'key': 'state_actions'},
215
+ {'action': 'blank'},
216
+ {'action': 'state_actions_impls'},
217
+ ],
218
+ 'statable_init.c': [
219
+ {'action': 'file_header',
220
+ 'filename': 'statable_init.c'},
221
+ {'action': 'blank'},
222
+ {'action': 'include_section', 'key': 'init_c'},
223
+ {'action': 'section_header', 'key': 'init_func'},
224
+ {'action': 'blank'},
225
+ {'action': 'init_func'},
226
+ {'action': 'blank'},
227
+ # [C-52] per-layer queue initialization
228
+ {'action': 'init_queues'},
229
+ ],
230
+ 'statable_event_queue.c': [
231
+ {'action': 'file_header',
232
+ 'filename': 'statable_event_queue.c'},
233
+ {'action': 'blank'},
234
+ {'action': 'include_section',
235
+ 'key': 'event_queue_c'},
236
+ {'action': 'event_queues'},
237
+ ],
238
+ 'statable_interrupt.c': [
239
+ {'action': 'file_header',
240
+ 'filename': 'statable_interrupt.c'},
241
+ {'action': 'blank'},
242
+ {'action': 'include_section',
243
+ 'key': 'interrupt_c'},
244
+ {'action': 'interrupts'},
245
+ ],
246
+ 'statable_timer.c': [
247
+ {'action': 'file_header',
248
+ 'filename': 'statable_timer.c'},
249
+ {'action': 'blank'},
250
+ {'action': 'include_section', 'key': 'timer_c'},
251
+ {'action': 'section_header', 'key': 'type_defs'},
252
+ {'action': 'blank'},
253
+ {'action': 'timer_struct'},
254
+ {'action': 'blank'},
255
+ {'action': 'section_header', 'key': 'init_func'},
256
+ {'action': 'blank'},
257
+ {'action': 'timer_init'},
258
+ {'action': 'blank'},
259
+ {'action': 'section_header',
260
+ 'key': 'transition_func'},
261
+ {'action': 'blank'},
262
+ {'action': 'timer_update'},
263
+ ],
264
+ 'osal.h': [
265
+ {'action': 'osal_header'},
266
+ ],
267
+ 'osal.c': [
268
+ {'action': 'osal_source'},
269
+ ],
270
+ 'statable_all.h': [
271
+ {'action': 'super_include_header'},
272
+ {'action': 'blank'},
273
+ {'action': 'super_include_guard_start'},
274
+ {'action': 'blank'},
275
+ {'action': 'super_include_common'},
276
+ {'action': 'blank'},
277
+ {'action': 'super_include_layer'},
278
+ {'action': 'blank'},
279
+ {'action': 'super_include_project'},
280
+ {'action': 'blank'},
281
+ {'action': 'super_include_extern_vars'},
282
+ {'action': 'blank'},
283
+ {'action': 'super_include_extern_funcs'},
284
+ {'action': 'blank'},
285
+ {'action': 'super_include_external',
286
+ 'when': lambda c: (
287
+ c['config'].external_includes_in_super
288
+ and bool(c['config'].external_includes)
289
+ )},
290
+ {'action': 'blank',
291
+ 'when': lambda c: (
292
+ c['config'].external_includes_in_super
293
+ and bool(c['config'].external_includes)
294
+ )},
295
+ {'action': 'super_include_user'},
296
+ {'action': 'blank'},
297
+ {'action': 'super_include_guard_end'},
298
+ ],
299
+ }
300
+
301
+ STRUCT_KIND_DISPATCH: Dict[str, str] = {
302
+ 'system_data': 'system_data',
303
+ 'event_flags': 'event_flags',
304
+ 'system_context': 'system_context',
305
+ 'common_transition_context': 'common_transition_context',
306
+ 'pending_event_macros': 'pending_event_macros',
307
+ # [C-52]
308
+ 'layer_queue_types': 'layer_queue_types',
309
+ 'layer_queue_macros': 'layer_queue_macros',
310
+ }
311
+
312
+ FILE_DISPATCH: Dict[str, str] = {
313
+ 'statable_types_common.h': '_generate_types_common_header',
314
+ 'statable_types.h': '_generate_types_header',
315
+ 'statable_transitions.h': '_generate_transitions_header',
316
+ 'statable_transitions.c': '_generate_transitions_source',
317
+ 'statable_role_functions.h': '_generate_role_functions_header',
318
+ 'statable_role_functions.c': '_generate_role_functions_source',
319
+ 'statable_state_actions.h': '_generate_state_actions_header',
320
+ 'statable_state_actions.c': '_generate_state_actions_source',
321
+ 'statable_init.c': '_generate_init_source',
322
+ 'statable_event_queue.c': '_generate_event_queue_source',
323
+ 'statable_interrupt.c': '_generate_interrupt_source',
324
+ 'statable_timer.c': '_generate_timer_source',
325
+ 'osal.h': '_generate_osal_header',
326
+ 'osal.c': '_generate_osal_source',
327
+ 'statable_all.h': '_generate_super_include',
328
+ }
329
+
330
+ FILE_CATEGORY: Dict[str, str] = {
331
+ 'statable_types_common.h': 'include',
332
+ 'statable_types.h': 'include',
333
+ 'statable_transitions.h': 'include',
334
+ 'statable_role_functions.h': 'include',
335
+ 'statable_state_actions.h': 'include',
336
+ 'statable_state_actions.c': 'src',
337
+ 'statable_transitions.c': 'src',
338
+ 'statable_role_functions.c': 'src',
339
+ 'statable_init.c': 'src',
340
+ 'statable_event_queue.c': 'src',
341
+ 'statable_interrupt.c': 'src',
342
+ 'statable_timer.c': 'src',
343
+ 'osal.h': 'common',
344
+ 'osal.c': 'common',
345
+ }
346
+
347
+ FOLDER_STRUCTURE_RESOLVERS: Dict[str, str] = {
348
+ 'flat': '_resolve_path_flat',
349
+ 'by_type': '_resolve_path_by_type',
350
+ 'by_layer': '_resolve_path_by_layer',
351
+ }
352
+
353
+ LAYER_SPECIFIC_FILES = {
354
+ 'statable_types.h',
355
+ 'statable_transitions.h',
356
+ 'statable_transitions.c',
357
+ 'statable_role_functions.h',
358
+ 'statable_role_functions.c',
359
+ 'statable_state_actions.h',
360
+ 'statable_state_actions.c',
361
+ }
362
+
363
+ COMMON_FILES = {
364
+ 'statable_types_common.h',
365
+ 'statable_init.c',
366
+ 'statable_event_queue.c',
367
+ 'statable_interrupt.c',
368
+ 'statable_timer.c',
369
+ 'osal.h',
370
+ 'osal.c',
371
+ 'statable_all.h',
372
+ }
373
+
374
+ def __init__(self,
375
+ config: Optional[CodeGenerationConfig] = None):
376
+ self.mapper = CTypeMapper()
377
+ self.naming = CNamingConvention()
378
+ self.struct_gen = CStructGenerator()
379
+ self.enum_gen = CEnumGenerator()
380
+ self.transition_gen = TransitionGenerator()
381
+ self.role_func_gen = RoleFunctionGenerator()
382
+ self.variable_gen = VariableGenerator()
383
+ self.event_queue_gen = EventQueueGenerator()
384
+ self.interrupt_gen = InterruptGenerator()
385
+ self.timer_gen = TimerGenerator()
386
+ self.osal_gen = OSALGenerator()
387
+ self.state_actions_gen = StateActionsGenerator()
388
+ self.templates = CodeTemplates()
389
+ self.strings = self.templates.STRINGS
390
+ self.formats = self.templates.FORMATS
391
+ self.merger = CodeMerger()
392
+
393
+ self.config_manager = ConfigManager()
394
+ if config:
395
+ self.config_manager.set_config(config)
396
+ self.config = self.config_manager.get_config()
397
+
398
+ self.generation_date = (
399
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S")
400
+ )
401
+
402
+ self._current_role_function_library = None
403
+
404
+ self._all_layers_for_includes: Optional[
405
+ List[Tuple[str, StateMachine]]
406
+ ] = None
407
+
408
+ self.super_loop_filename = (
409
+ f"{self.config.project_name}_run.c"
410
+ )
411
+
412
+ self.FILE_STEPS = dict(self.__class__.FILE_STEPS)
413
+ self.FILE_DISPATCH = dict(self.__class__.FILE_DISPATCH)
414
+ self.FILE_CATEGORY = dict(self.__class__.FILE_CATEGORY)
415
+
416
+ self.FILE_STEPS[self.super_loop_filename] = [
417
+ {'action': 'super_loop_header'},
418
+ {'action': 'blank'},
419
+ {'action': 'super_loop_include'},
420
+ {'action': 'blank'},
421
+ {'action': 'super_loop_context_var'},
422
+ {'action': 'blank'},
423
+ {'action': 'super_loop_state_var'},
424
+ {'action': 'blank'},
425
+ {'action': 'super_loop_init_func'},
426
+ {'action': 'blank'},
427
+ {'action': 'super_loop_run_func'},
428
+ ]
429
+ self.FILE_DISPATCH[self.super_loop_filename] = \
430
+ '_generate_super_loop'
431
+ self.FILE_CATEGORY[self.super_loop_filename] = 'src'
432
+
433
+ self.file_generators: Dict[str, Dict] = {
434
+ 'statable_types_common.h': {
435
+ 'description': 'Common type definitions for state transition system',
436
+ 'guard_name': 'STATABLE_TYPES_COMMON_H',
437
+ },
438
+ 'statable_types.h': {
439
+ 'description': 'Layer-specific type definitions (enum)',
440
+ 'guard_name': 'STATABLE_TYPES_H',
441
+ },
442
+ 'statable_transitions.h': {
443
+ 'description': 'State transition function declarations',
444
+ 'guard_name': 'STATABLE_TRANSITIONS_H',
445
+ },
446
+ 'statable_transitions.c': {
447
+ 'description': 'State transition logic',
448
+ 'guard_name': None,
449
+ },
450
+ 'statable_role_functions.h': {
451
+ 'description': 'Role function declarations',
452
+ 'guard_name': 'STATABLE_ROLE_FUNCTIONS_H',
453
+ },
454
+ 'statable_role_functions.c': {
455
+ 'description': 'Role function implementations',
456
+ 'guard_name': None,
457
+ },
458
+ 'statable_state_actions.h': {
459
+ 'description': 'State actions (Entry / Exit / Do) declarations',
460
+ 'guard_name': 'STATABLE_STATE_ACTIONS_H',
461
+ },
462
+ 'statable_state_actions.c': {
463
+ 'description': 'State actions (Entry / Exit / Do) implementations',
464
+ 'guard_name': None,
465
+ },
466
+ 'statable_init.c': {
467
+ 'description': 'Initialization processing',
468
+ 'guard_name': None,
469
+ },
470
+ 'statable_event_queue.c': {
471
+ 'description': 'Event queue implementation',
472
+ 'guard_name': None,
473
+ },
474
+ 'statable_interrupt.c': {
475
+ 'description': 'Interrupt handler ISR',
476
+ 'guard_name': None,
477
+ },
478
+ 'statable_timer.c': {
479
+ 'description': 'Timer processing',
480
+ 'guard_name': None,
481
+ },
482
+ 'osal.h': {
483
+ 'description': 'OSAL header',
484
+ 'guard_name': 'OSAL_H',
485
+ },
486
+ 'osal.c': {
487
+ 'description': 'OSAL source',
488
+ 'guard_name': None,
489
+ },
490
+ 'statable_all.h': {
491
+ 'description': 'StaTable super include',
492
+ 'guard_name': 'STATABLE_ALL_H',
493
+ },
494
+ self.super_loop_filename: {
495
+ 'description': 'State machine super loop',
496
+ 'guard_name': None,
497
+ },
498
+ }
499
+
500
+ # [v2.2.9 / MISRA 17.3 fix] statable_types_common.h added to
501
+ # transitions_c so LOG_* macros are visible in file scope.
502
+ self.include_headers: Dict[str, List[str]] = {
503
+ 'types': [
504
+ '#include <stdint.h>',
505
+ '#include <stdbool.h>',
506
+ '#include <string.h>',
507
+ ],
508
+ 'types_layer': [
509
+ '#include "statable_types_common.h"',
510
+ ],
511
+ 'transitions_h': [
512
+ '#include "statable_types{layer_suffix}.h"',
513
+ ],
514
+ 'transitions_c': [
515
+ '#include "statable_transitions{layer_suffix}.h"',
516
+ '#include "statable_role_functions{layer_suffix}.h"',
517
+ '#include "statable_state_actions{layer_suffix}.h"',
518
+ '#include "statable_types_common.h"',
519
+ ],
520
+ 'role_functions_h': [
521
+ '#include "statable_types{layer_suffix}.h"',
522
+ ],
523
+ 'role_functions_c': [
524
+ '#include "statable_role_functions{layer_suffix}.h"',
525
+ ],
526
+ 'state_actions_h': [
527
+ '#include "statable_types_common.h"',
528
+ '#include "statable_types{layer_suffix}.h"',
529
+ ],
530
+ 'state_actions_c': [
531
+ '#include "statable_state_actions{layer_suffix}.h"',
532
+ '#include "statable_role_functions{layer_suffix}.h"',
533
+ '#include "statable_types_common.h"',
534
+ ],
535
+ 'init_c': ['#include "statable_types_common.h"'],
536
+ 'event_queue_c': ['#include "statable_types_common.h"'],
537
+ 'interrupt_c': [
538
+ '#include "statable_types_common.h"',
539
+ '#include "statable_all.h"',
540
+ ],
541
+ 'timer_c': ['#include "statable_types_common.h"'],
542
+ }
543
+
544
+ self.step_executors: Dict[str, Callable] = {
545
+ 'file_header': self._step_file_header,
546
+ 'blank': self._step_blank,
547
+ 'guard_start': self._step_guard_start,
548
+ 'guard_end': self._step_guard_end,
549
+ 'include_section': self._step_include_section,
550
+ 'section_header': self._step_section_header,
551
+ 'enums': self._step_enums,
552
+ 'enums_common': self._step_enums_common,
553
+ 'layer_transition_context': self._step_layer_transition_context,
554
+ 'common_function_decls': self._step_common_function_decls,
555
+ 'custom_types': self._step_custom_types,
556
+ 'struct': self._step_struct,
557
+ 'var_macros': self._step_var_macros,
558
+ 'state_machine_decl': self._step_state_machine_decl,
559
+ 'cell_prototypes': self._step_cell_prototypes,
560
+ 'transition_table': self._step_transition_table,
561
+ 'cell_functions': self._step_cell_functions,
562
+ 'process_func': self._step_process_func,
563
+ 'get_next_event': self._step_get_next_event,
564
+ 'role_decls': self._step_role_decls,
565
+ 'role_impls': self._step_role_impls,
566
+ 'state_actions_decls': self._step_state_actions_decls,
567
+ 'state_actions_impls': self._step_state_actions_impls,
568
+ 'init_func': self._step_init_func,
569
+ 'init_queues': self._step_init_queues,
570
+ 'event_queues': self._step_event_queues,
571
+ 'interrupts': self._step_interrupts,
572
+ 'timer_struct': self._step_timer_struct,
573
+ 'timer_init': self._step_timer_init,
574
+ 'timer_update': self._step_timer_update,
575
+ 'osal_header': self._step_osal_header,
576
+ 'osal_source': self._step_osal_source,
577
+ 'super_include_header': self._step_super_include_header,
578
+ 'super_include_guard_start': self._step_super_include_guard_start,
579
+ 'super_include_common': self._step_super_include_common,
580
+ 'super_include_layer': self._step_super_include_layer,
581
+ 'super_include_project': self._step_super_include_project,
582
+ 'super_include_extern_vars': self._step_super_include_extern_vars,
583
+ 'super_include_extern_funcs': self._step_super_include_extern_funcs,
584
+ 'super_include_external': self._step_super_include_external,
585
+ 'super_include_user': self._step_super_include_user,
586
+ 'super_include_guard_end': self._step_super_include_guard_end,
587
+ 'super_loop_header': self._step_super_loop_header,
588
+ 'super_loop_include': self._step_super_loop_include,
589
+ 'super_loop_context_var': self._step_super_loop_context_var,
590
+ 'super_loop_state_var': self._step_super_loop_state_var,
591
+ 'super_loop_init_func': self._step_super_loop_init_func,
592
+ 'super_loop_run_func': self._step_super_loop_run_func,
593
+ }
594
+
595
+ def _log_debug(self, message, level='debug'):
596
+ log_func = getattr(logger, level, logger.debug)
597
+ log_func(message)
598
+
599
+ def get_config(self) -> CodeGenerationConfig:
600
+ return self.config
601
+
602
+ def set_config(self, config: CodeGenerationConfig):
603
+ self.config_manager.set_config(config)
604
+ self.config = self.config_manager.get_config()
605
+
606
+ def update_config(self, **kwargs):
607
+ self.config_manager.update(**kwargs)
608
+ self.config = self.config_manager.get_config()
609
+
610
+ def reset_config(self):
611
+ self.config_manager.reset()
612
+ self.config = self.config_manager.get_config()
613
+
614
+ def _get_states_list(self, state_machine):
615
+ return list(state_machine.states.values())
616
+
617
+ def _get_events_list(self, state_machine):
618
+ return list(state_machine.events.values())
619
+
620
+ def _get_role_functions_list(self, state_machine):
621
+ funcs = dict(state_machine.role_functions)
622
+ lib = self._current_role_function_library
623
+ if lib is not None:
624
+ for rf in lib.list_all():
625
+ name = getattr(rf, 'name', None)
626
+ if name and name not in funcs:
627
+ funcs[name] = rf
628
+ return list(funcs.values())
629
+
630
+ def _get_layer_name(self, state_machine) -> str:
631
+ return getattr(state_machine, 'layer_name', '') or ''
632
+
633
+ def _get_initial_state(self, state_machine) -> str:
634
+ initial = getattr(state_machine, 'initial_state', None)
635
+ if not initial:
636
+ initial = getattr(state_machine, 'initial', None)
637
+ return initial or 'Idle'
638
+
639
+ def _generate_section_header(self, section_key):
640
+ start = self.strings['section_line_start']
641
+ end = self.strings['section_line_end']
642
+ title = self.templates.SECTION_HEADERS.get(section_key, '')
643
+ return f"{start}\n * {title}\n{end}"
644
+
645
+ def _generate_file_header(self, filename, description=""):
646
+ return (
647
+ f"/**\n"
648
+ f" * @file {filename}\n"
649
+ f" * @brief {description}\n"
650
+ f" *\n"
651
+ f" * @note {self.strings['auto_generated']}\n"
652
+ f" * - {self.strings['no_edit']}\n"
653
+ f" * - {self.strings['edit_in_statable']}\n"
654
+ f" *\n"
655
+ f" * @date {self.generation_date}\n"
656
+ f" */"
657
+ )
658
+
659
+ def _generate_include_guard_start(self, guard_name):
660
+ return f"#ifndef {guard_name}\n#define {guard_name}\n"
661
+
662
+ def _generate_include_guard_end(self, guard_name):
663
+ return f"#endif /* {guard_name} */"
664
+
665
+ def _generate_include_section(self, include_key, layer_suffix=''):
666
+ lines = [self._generate_section_header('include'), ""]
667
+ for header in self.include_headers.get(include_key, []):
668
+ header = header.replace('{layer_suffix}', layer_suffix)
669
+ lines.append(header)
670
+ lines.append("")
671
+ return '\n'.join(lines)
672
+
673
+ def _layer_filename(self, filename: str, layer_name: str) -> str:
674
+ if not layer_name:
675
+ return filename
676
+ if filename not in self.LAYER_SPECIFIC_FILES:
677
+ return filename
678
+ stem, ext = os.path.splitext(filename)
679
+ return f"{stem}_{layer_name}{ext}"
680
+
681
+ def _layer_suffix(self, layer_name: str) -> str:
682
+ return f"_{layer_name}" if layer_name else ""
683
+
684
+ def _normalize_layers(self, layers) -> List[Tuple[str, StateMachine]]:
685
+ if layers is None:
686
+ return []
687
+ if isinstance(layers, StateMachine):
688
+ name = self._get_layer_name(layers)
689
+ return [(name, layers)]
690
+ result = []
691
+ for item in layers:
692
+ if isinstance(item, tuple) and len(item) == 2:
693
+ name, sm = item
694
+ elif isinstance(item, StateMachine):
695
+ name = self._get_layer_name(item)
696
+ sm = item
697
+ else:
698
+ continue
699
+ if not self._get_layer_name(sm) and name:
700
+ sm.layer_name = name
701
+ result.append((name, sm))
702
+ result.sort(key=lambda x: getattr(x[1], 'layer_priority', 5))
703
+ return result
704
+
705
+ def _setup_layer_generators(self, state_machine):
706
+ layer_name = self._get_layer_name(state_machine)
707
+ for name, gen in [
708
+ ('enum_gen', self.enum_gen),
709
+ ('transition_gen', self.transition_gen),
710
+ ('role_func_gen', self.role_func_gen),
711
+ ('state_actions_gen', self.state_actions_gen),
712
+ ('struct_gen', self.struct_gen),
713
+ ('interrupt_gen', self.interrupt_gen),
714
+ ]:
715
+ if hasattr(gen, 'set_layer'):
716
+ gen.set_layer(layer_name)
717
+
718
+ def _resolve_output_path(self, filename: str,
719
+ layer_name: str = '') -> str:
720
+ if '/' in filename or '\\' in filename:
721
+ return filename
722
+ if filename == 'statable_all.h':
723
+ return self._resolve_super_include_path(layer_name)
724
+ structure = self.config.folder_structure
725
+ resolver_name = self.FOLDER_STRUCTURE_RESOLVERS.get(
726
+ structure, '_resolve_path_flat'
727
+ )
728
+ resolver = getattr(self, resolver_name,
729
+ self._resolve_path_flat)
730
+ return resolver(filename, layer_name)
731
+
732
+ def _resolve_super_include_path(self, layer_name: str = '') -> str:
733
+ fname = self.config.super_include_file
734
+ structure = self.config.folder_structure
735
+ if structure == 'flat':
736
+ return fname
737
+ elif structure == 'by_type':
738
+ return os.path.join(
739
+ self.config.super_include_dir, fname
740
+ )
741
+ elif structure == 'by_layer':
742
+ if layer_name:
743
+ return os.path.join(layer_name, fname)
744
+ return fname
745
+ return fname
746
+
747
+ def _resolve_path_flat(self, filename: str,
748
+ layer_name: str = '') -> str:
749
+ return filename
750
+
751
+ def _resolve_path_by_type(self, filename: str,
752
+ layer_name: str = '') -> str:
753
+ category = self.FILE_CATEGORY.get(filename, '')
754
+ if category == 'include':
755
+ return os.path.join(
756
+ self.config.include_dir_name, filename
757
+ )
758
+ elif category == 'src':
759
+ return os.path.join(
760
+ self.config.source_dir_name, filename
761
+ )
762
+ elif category == 'common':
763
+ return os.path.join(
764
+ self.config.common_dir_name, filename
765
+ )
766
+ return filename
767
+
768
+ def _resolve_path_by_layer(self, filename: str,
769
+ layer_name: str = '') -> str:
770
+ if not layer_name:
771
+ return filename
772
+ if filename in self.LAYER_SPECIFIC_FILES:
773
+ fname = self._layer_filename(filename, layer_name)
774
+ return os.path.join(layer_name, fname)
775
+ return filename
776
+
777
+ def _run_steps(self, filename,
778
+ state_machine, global_defs) -> str:
779
+ return self._run_steps_multi(
780
+ filename,
781
+ [(self._get_layer_name(state_machine), state_machine)],
782
+ global_defs
783
+ )
784
+
785
+ def _run_steps_multi(self, filename,
786
+ layers: List[Tuple[str, StateMachine]],
787
+ global_defs) -> str:
788
+ if not layers:
789
+ return ""
790
+ file_config = self.file_generators[filename]
791
+ steps = self.FILE_STEPS.get(filename, [])
792
+ context = {
793
+ 'layers': layers,
794
+ 'state_machine': layers[0][1],
795
+ 'global_defs': global_defs,
796
+ 'file_config': file_config,
797
+ 'filename': filename,
798
+ 'config': self.config,
799
+ 'is_multi': len(layers) > 1,
800
+ }
801
+ parts: List[str] = []
802
+ for step in steps:
803
+ when = step.get('when')
804
+ if when is not None and not when(context):
805
+ continue
806
+ action = step.get('action', '')
807
+ executor = self.step_executors.get(action)
808
+ if executor is None:
809
+ self._log_debug(
810
+ f"Unknown step action: {action}", 'warning'
811
+ )
812
+ continue
813
+ result = executor(step, context)
814
+ if result is None:
815
+ continue
816
+ if isinstance(result, list):
817
+ parts.extend(result)
818
+ else:
819
+ parts.append(result)
820
+ return '\n'.join(parts)
821
+
822
+ def _step_file_header(self, step, ctx):
823
+ filename = step.get('filename', ctx['filename'])
824
+ layers = ctx.get('layers', [])
825
+ if (filename in self.LAYER_SPECIFIC_FILES and layers):
826
+ layer_name = self._get_layer_name(layers[0][1])
827
+ filename = self._layer_filename(filename, layer_name)
828
+ desc = ctx['file_config'].get('description', '')
829
+ return [self._generate_file_header(filename, desc)]
830
+
831
+ def _step_blank(self, step, ctx):
832
+ return [""]
833
+
834
+ def _step_guard_start(self, step, ctx):
835
+ guard = ctx['file_config'].get('guard_name')
836
+ if not guard:
837
+ return []
838
+ filename = ctx.get('filename', '')
839
+ layers = ctx.get('layers', [])
840
+ if filename in self.LAYER_SPECIFIC_FILES and layers:
841
+ layer_name = self._get_layer_name(layers[0][1])
842
+ if layer_name:
843
+ guard = f"{guard}_{layer_name.upper()}"
844
+ return [self._generate_include_guard_start(guard)]
845
+
846
+ def _step_guard_end(self, step, ctx):
847
+ guard = ctx['file_config'].get('guard_name')
848
+ if not guard:
849
+ return []
850
+ filename = ctx.get('filename', '')
851
+ layers = ctx.get('layers', [])
852
+ if filename in self.LAYER_SPECIFIC_FILES and layers:
853
+ layer_name = self._get_layer_name(layers[0][1])
854
+ if layer_name:
855
+ guard = f"{guard}_{layer_name.upper()}"
856
+ return [self._generate_include_guard_end(guard)]
857
+
858
+ def _step_include_section(self, step, ctx):
859
+ key = step.get('key', '')
860
+ filename = ctx.get('filename', '')
861
+ layers = ctx.get('layers', [])
862
+ structure = ctx['config'].folder_structure
863
+
864
+ if filename in self.LAYER_SPECIFIC_FILES and layers:
865
+ layer_name = self._get_layer_name(layers[0][1])
866
+ suffix = self._layer_suffix(layer_name)
867
+
868
+ if (structure == 'by_layer'
869
+ and filename in ('statable_transitions.c',
870
+ 'statable_role_functions.c',
871
+ 'statable_state_actions.c')):
872
+ lines = [self._generate_section_header('include'), ""]
873
+ for header in self.include_headers.get(key, []):
874
+ header = header.replace('{layer_suffix}', suffix)
875
+ lines.append(header)
876
+ all_layers = (self._all_layers_for_includes
877
+ or layers)
878
+ for other_name, other_sm in all_layers:
879
+ other_layer = self._get_layer_name(other_sm)
880
+ if not other_layer or other_layer == layer_name:
881
+ continue
882
+ lines.append(
883
+ f'#include "{other_layer}/'
884
+ f'statable_role_functions_{other_layer}.h"'
885
+ )
886
+ lines.append("")
887
+ return ['\n'.join(lines)]
888
+
889
+ return [self._generate_include_section(
890
+ key, layer_suffix=suffix
891
+ )]
892
+
893
+ if structure == 'by_layer' and filename in (
894
+ 'statable_init.c',
895
+ 'statable_event_queue.c',
896
+ 'statable_interrupt.c',
897
+ 'statable_timer.c'):
898
+ lines = [self._generate_section_header('include'), ""]
899
+ if filename == 'statable_interrupt.c':
900
+ lines.append('#include "statable_all.h"')
901
+ else:
902
+ lines.append('#include "statable_types_common.h"')
903
+ for layer_name, sm in layers:
904
+ layer = self._get_layer_name(sm)
905
+ if layer:
906
+ lines.append(
907
+ f'#include "{layer}/'
908
+ f'statable_types_{layer}.h"'
909
+ )
910
+ lines.append("")
911
+ return ['\n'.join(lines)]
912
+
913
+ return [self._generate_include_section(key)]
914
+
915
+ def _step_section_header(self, step, ctx):
916
+ return [self._generate_section_header(
917
+ step.get('key', '')
918
+ )]
919
+
920
+ def _step_enums(self, step, ctx):
921
+ gd = ctx['global_defs']
922
+ layers = ctx['layers']
923
+ results = []
924
+ for layer_name, sm in layers:
925
+ self._setup_layer_generators(sm)
926
+ enum_code = self.enum_gen.generate_all_enums(
927
+ self._get_states_list(sm),
928
+ self._get_events_list(sm),
929
+ None,
930
+ )
931
+ if enum_code:
932
+ results.append(enum_code)
933
+ if not results:
934
+ return ['']
935
+ return ['\n'.join(results)]
936
+
937
+ def _step_enums_common(self, step, ctx):
938
+ gd = ctx['global_defs']
939
+ flags = getattr(gd, 'flags', []) or []
940
+ if not flags:
941
+ return ['']
942
+ self.enum_gen.set_layer("")
943
+ code = self.enum_gen.generate_flag_enum(flags)
944
+ return [code] if code else ['']
945
+
946
+ def _step_layer_transition_context(self, step, ctx):
947
+ layers = ctx['layers']
948
+ results = []
949
+ for layer_name, sm in layers:
950
+ layer = self._get_layer_name(sm)
951
+ if not layer:
952
+ continue
953
+ self.struct_gen.set_layer(layer)
954
+ state_type = f"STATE_{layer}_t"
955
+ event_type = f"EVENT_{layer}_t"
956
+ code = self.struct_gen.generate_layer_transition_context(
957
+ state_type, event_type,
958
+ )
959
+ if code:
960
+ results.append(code)
961
+ return ['\n\n'.join(results)] if results else ['']
962
+
963
+ def _step_common_function_decls(self, step, ctx):
964
+ return [
965
+ '/* ---- Logging macros (default: no-op) ---- */',
966
+ '/*',
967
+ ' * These are safe defaults. To enable real logging,',
968
+ ' * define the macros before including this header.',
969
+ ' */',
970
+ '#ifndef LOG_DEBUG',
971
+ '#define LOG_DEBUG(...) ((void)0)',
972
+ '#endif',
973
+ '',
974
+ '#ifndef LOG_INFO',
975
+ '#define LOG_INFO(...) ((void)0)',
976
+ '#endif',
977
+ '',
978
+ '#ifndef LOG_WARNING',
979
+ '#define LOG_WARNING(...) ((void)0)',
980
+ '#endif',
981
+ '',
982
+ '#ifndef LOG_ERROR',
983
+ '#define LOG_ERROR(...) ((void)0)',
984
+ '#endif',
985
+ '',
986
+ '/**',
987
+ ' * @brief Initialize SystemContext_t (implemented in statable_init.c)',
988
+ ' * @param ctx System context pointer',
989
+ ' */',
990
+ 'void SystemContext_Init(SystemContext_t *ctx);',
991
+ '',
992
+ '/**',
993
+ ' * @brief Initialize per-layer event queues (C-52)',
994
+ ' * @param ctx System context pointer',
995
+ ' */',
996
+ 'void SystemContext_InitQueues(SystemContext_t *ctx);',
997
+ '',
998
+ '/**',
999
+ ' * @brief Initialize timer variables (implemented in statable_timer.c)',
1000
+ ' * @param ctx System context pointer',
1001
+ ' */',
1002
+ 'void Timer_Init(SystemContext_t *ctx);',
1003
+ '',
1004
+ '/**',
1005
+ ' * @brief Update derived timer variables (implemented in statable_timer.c)',
1006
+ ' * @param ctx System context pointer',
1007
+ ' */',
1008
+ 'void Timer_Update(SystemContext_t *ctx);',
1009
+ ]
1010
+
1011
+ def _step_custom_types(self, step, ctx):
1012
+ gd = ctx['global_defs']
1013
+ result: List[str] = [
1014
+ self._generate_section_header('custom_types'),
1015
+ "",
1016
+ ]
1017
+ for custom_type in gd.custom_types:
1018
+ result.append(self.struct_gen.generate_struct(
1019
+ 'custom_type', custom_type
1020
+ ))
1021
+ result.append("")
1022
+ return result
1023
+
1024
+ def _step_struct(self, step, ctx):
1025
+ kind = step.get('kind', '')
1026
+ method_name = self.STRUCT_KIND_DISPATCH.get(kind)
1027
+ if method_name is None:
1028
+ return []
1029
+ # [C-52] propagate layer list for queue members/macros
1030
+ self.struct_gen.set_layers(ctx.get('layers', []))
1031
+ return [self.struct_gen.generate_struct(
1032
+ kind, ctx['global_defs']
1033
+ )]
1034
+
1035
+ def _step_var_macros(self, step, ctx):
1036
+ return [self.variable_gen.generate_all_macros(
1037
+ ctx['global_defs']
1038
+ )]
1039
+
1040
+ def _step_state_machine_decl(self, step, ctx):
1041
+ layers = ctx['layers']
1042
+ results = []
1043
+ for layer_name, sm in layers:
1044
+ layer = self._get_layer_name(sm)
1045
+ if layer:
1046
+ state_type = f"STATE_{layer}_t"
1047
+ event_type = f"EVENT_{layer}_t"
1048
+ process_name = f"StateMachine_Process_{layer}"
1049
+ get_evt_name = f"StateMachine_GetNextEvent_{layer}"
1050
+ event_none = f"EVENT_{layer}_NONE"
1051
+ else:
1052
+ state_type = "STATE_t"
1053
+ event_type = "EVENT_t"
1054
+ process_name = "StateMachine_Process"
1055
+ get_evt_name = "StateMachine_GetNextEvent"
1056
+ event_none = "EVENT_NONE"
1057
+
1058
+ results.append('\n'.join([
1059
+ "/**",
1060
+ " * @brief State transition processing",
1061
+ " * @param current_state Current state",
1062
+ " * @param event Event that occurred",
1063
+ " * @param ctx System context pointer",
1064
+ " * @return State after transition",
1065
+ " */",
1066
+ f"{state_type} {process_name}(",
1067
+ f" {state_type} current_state,",
1068
+ f" {event_type} event,",
1069
+ " SystemContext_t *ctx",
1070
+ ");",
1071
+ "",
1072
+ "/**",
1073
+ " * @brief Get next event for this layer",
1074
+ " * @param ctx System context pointer",
1075
+ f" * @return Next event ({event_none} if none pending)",
1076
+ " */",
1077
+ f"{event_type} {get_evt_name}(SystemContext_t *ctx);",
1078
+ ]))
1079
+ return ['\n'.join(results)] if results else ['']
1080
+
1081
+ def _step_cell_prototypes(self, step, ctx):
1082
+ layers = ctx['layers']
1083
+ results = []
1084
+ for layer_name, sm in layers:
1085
+ self._setup_layer_generators(sm)
1086
+ proto = (self.transition_gen
1087
+ .generate_transition_cell_prototypes(sm))
1088
+ if proto:
1089
+ results.append(proto)
1090
+ return ['\n'.join(results)] if results else ['']
1091
+
1092
+ def _step_transition_table(self, step, ctx):
1093
+ layers = ctx['layers']
1094
+ results = []
1095
+ for layer_name, sm in layers:
1096
+ self._setup_layer_generators(sm)
1097
+ tbl = self.transition_gen.generate_transition_table(
1098
+ sm, table_type=ctx['config'].table_type,
1099
+ )
1100
+ if tbl:
1101
+ results.append(tbl)
1102
+ return ['\n'.join(results)] if results else ['']
1103
+
1104
+ def _step_cell_functions(self, step, ctx):
1105
+ layers = ctx['layers']
1106
+ results = []
1107
+ for layer_name, sm in layers:
1108
+ self._setup_layer_generators(sm)
1109
+ funcs = (self.transition_gen
1110
+ .generate_transition_cell_functions(sm))
1111
+ if funcs:
1112
+ results.append(funcs)
1113
+ return ['\n'.join(results)] if results else ['']
1114
+
1115
+ def _step_process_func(self, step, ctx):
1116
+ layers = ctx['layers']
1117
+ results = []
1118
+ for layer_name, sm in layers:
1119
+ self._setup_layer_generators(sm)
1120
+ func = self.transition_gen.generate_process_function(
1121
+ sm,
1122
+ generation_style=ctx['config'].generation_style,
1123
+ )
1124
+ if func:
1125
+ results.append(func)
1126
+ return ['\n'.join(results)] if results else ['']
1127
+
1128
+ def _step_get_next_event(self, step, ctx):
1129
+ layers = ctx['layers']
1130
+ results = []
1131
+ for layer_name, sm in layers:
1132
+ self._setup_layer_generators(sm)
1133
+ func = self.transition_gen.generate_get_next_event_function(sm)
1134
+ if func:
1135
+ results.append(func)
1136
+ return ['\n'.join(results)] if results else ['']
1137
+
1138
+ def _step_role_decls(self, step, ctx):
1139
+ layers = ctx['layers']
1140
+ results = []
1141
+ for layer_name, sm in layers:
1142
+ self._setup_layer_generators(sm)
1143
+ decls = (self.role_func_gen
1144
+ .generate_all_declarations(
1145
+ self._get_role_functions_list(sm),
1146
+ state_machine=sm))
1147
+ if decls:
1148
+ results.append(decls)
1149
+ return ['\n'.join(results)] if results else ['']
1150
+
1151
+ def _step_state_actions_decls(self, step, ctx):
1152
+ """[v2.7.0] Emit declarations for statable_state_actions_<Layer>.h."""
1153
+ layers = ctx['layers']
1154
+ results = []
1155
+ for layer_name, sm in layers:
1156
+ layer = self._get_layer_name(sm) or layer_name
1157
+ if not layer:
1158
+ continue
1159
+ self.state_actions_gen.set_layer(layer)
1160
+ decls = (
1161
+ f"void {layer}_Entry(STATE_{layer}_t state, SystemContext_t *ctx);\n"
1162
+ f"void {layer}_Exit (STATE_{layer}_t state, SystemContext_t *ctx);\n"
1163
+ f"void {layer}_Do (STATE_{layer}_t state, SystemContext_t *ctx);"
1164
+ )
1165
+ results.append(decls)
1166
+ return ['\n'.join(results)] if results else ['']
1167
+
1168
+ def _step_state_actions_impls(self, step, ctx):
1169
+ """[v2.7.0] Emit implementations for statable_state_actions_<Layer>.c."""
1170
+ layers = ctx['layers']
1171
+ results = []
1172
+ for layer_name, sm in layers:
1173
+ layer = self._get_layer_name(sm) or layer_name
1174
+ if not layer:
1175
+ continue
1176
+ self.state_actions_gen.set_layer(layer)
1177
+ body = self._generate_state_actions_body(sm, layer)
1178
+ results.append(body)
1179
+ return ['\n'.join(results)] if results else ['']
1180
+
1181
+ def _generate_state_actions_body(self, sm, layer: str) -> str:
1182
+ """Body of statable_state_actions_<Layer>.c (no header/include)."""
1183
+ gen = self.state_actions_gen
1184
+ parts = []
1185
+ parts.append(f"typedef void (*{layer}_StateFunc_t)(SystemContext_t *ctx);")
1186
+ parts.append("")
1187
+ parts.append(gen._generate_forward_decls(sm))
1188
+ parts.append("")
1189
+ for kind in ("Entry", "Exit", "Do"):
1190
+ parts.append(f"/* ---- {kind} dispatch table ---- */")
1191
+ parts.append(gen._generate_table(kind, sm))
1192
+ parts.append("")
1193
+ for kind in ("Entry", "Exit", "Do"):
1194
+ parts.append(f"/* ---- {kind} dispatcher ---- */")
1195
+ parts.append(gen._generate_dispatcher(kind, sm))
1196
+ parts.append("")
1197
+ for kind in ("Entry", "Exit", "Do"):
1198
+ actions_by_state = gen._collect_actions_by_kind(sm, kind)
1199
+ parts.append(f"/* ---- {kind} functions (user-editable) ---- */")
1200
+ for state in sm.states.values():
1201
+ parts.append(gen._generate_state_func(
1202
+ kind, state, actions_by_state.get(state.name, [])))
1203
+ parts.append("")
1204
+ return "\n".join(parts)
1205
+
1206
+ def _generate_state_actions_header(self, sm, gd):
1207
+ """[v2.7.0] statable_state_actions_<Layer>.h generation."""
1208
+ return self._run_steps('statable_state_actions.h', sm, gd)
1209
+
1210
+ def _generate_state_actions_source(self, sm, gd):
1211
+ """[v2.7.0] statable_state_actions_<Layer>.c generation."""
1212
+ return self._run_steps('statable_state_actions.c', sm, gd)
1213
+
1214
+ def _step_role_impls(self, step, ctx):
1215
+ layers = ctx['layers']
1216
+ results = []
1217
+ for layer_name, sm in layers:
1218
+ self._setup_layer_generators(sm)
1219
+ impls = (self.role_func_gen
1220
+ .generate_all_implementations(
1221
+ self._get_role_functions_list(sm),
1222
+ state_machine=sm,
1223
+ global_defs=ctx['global_defs'],
1224
+ ))
1225
+ if impls:
1226
+ results.append(impls)
1227
+ return ['\n'.join(results)] if results else ['']
1228
+
1229
+ def _step_init_func(self, step, ctx):
1230
+ return [self.variable_gen.generate_init_function(
1231
+ ctx['global_defs']
1232
+ )]
1233
+
1234
+ def _step_init_queues(self, step, ctx):
1235
+ """[C-52] Emit SystemContext_InitQueues()."""
1236
+ layers = ctx.get('layers', [])
1237
+ if not layers:
1238
+ return ['']
1239
+ parts = [
1240
+ '/**',
1241
+ ' * @brief Initialize per-layer event queues (C-52)',
1242
+ ' * @param ctx System context pointer',
1243
+ ' */',
1244
+ 'void SystemContext_InitQueues(SystemContext_t *ctx)',
1245
+ '{',
1246
+ ' if (ctx == NULL) {',
1247
+ ' return;',
1248
+ ' }',
1249
+ ]
1250
+ seen = set()
1251
+ for layer_name, sm in layers:
1252
+ layer = self._get_layer_name(sm) or layer_name
1253
+ if not layer or layer in seen:
1254
+ continue
1255
+ seen.add(layer)
1256
+ # [F-3 Step 2] reset per-layer pending slot
1257
+ parts.append(f' ctx->pending_event_{layer} = 0U;')
1258
+ parts.append(f' ctx->pending_event_valid_{layer} = false;')
1259
+ parts.append(f' INIT_EVENT_QUEUE_{layer}(ctx);')
1260
+ parts.append('}')
1261
+ return ['\n'.join(parts)]
1262
+
1263
+ def _step_event_queues(self, step, ctx):
1264
+ gd = ctx['global_defs']
1265
+ queues = getattr(gd, 'event_queues', [])
1266
+ if not queues:
1267
+ return ["/* No event queue definitions */"]
1268
+ result: List[str] = [
1269
+ self._generate_section_header('type_defs'),
1270
+ "",
1271
+ ]
1272
+ for queue in queues:
1273
+ result.append(
1274
+ self.event_queue_gen.generate_all_code(queue)
1275
+ )
1276
+ result.append("")
1277
+ return result
1278
+
1279
+ def _step_interrupts(self, step, ctx):
1280
+ gd = ctx['global_defs']
1281
+ interrupts = getattr(gd, 'interrupts', [])
1282
+ if not interrupts:
1283
+ return ["/* No interrupt handler definitions */"]
1284
+ result: List[str] = [
1285
+ self._generate_section_header('transition_func'),
1286
+ "",
1287
+ ]
1288
+ for handler in interrupts:
1289
+ try:
1290
+ self.interrupt_gen.update_handler_symbols(handler)
1291
+ except Exception as e:
1292
+ self._log_debug(
1293
+ f"update_handler_symbols failed for "
1294
+ f"'{getattr(handler, 'name', '?')}': {e}",
1295
+ 'warning',
1296
+ )
1297
+ result.append(
1298
+ self.interrupt_gen.generate_isr(handler)
1299
+ )
1300
+ result.append("")
1301
+ return result
1302
+
1303
+ def _step_timer_struct(self, step, ctx):
1304
+ return [self.timer_gen.generate_struct(
1305
+ ctx['global_defs']
1306
+ )]
1307
+
1308
+ def _step_timer_init(self, step, ctx):
1309
+ return [self.timer_gen.generate_init_function(
1310
+ ctx['global_defs']
1311
+ )]
1312
+
1313
+ def _step_timer_update(self, step, ctx):
1314
+ return [self.timer_gen.generate_update_function(
1315
+ ctx['global_defs']
1316
+ )]
1317
+
1318
+ def _step_osal_header(self, step, ctx):
1319
+ return [self.osal_gen.generate_header(
1320
+ self.config.os_type
1321
+ )]
1322
+
1323
+ def _step_osal_source(self, step, ctx):
1324
+ return [self.osal_gen.generate_source(
1325
+ self.config.os_type
1326
+ )]
1327
+
1328
+ def _step_super_include_header(self, step, ctx):
1329
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1330
+ fname = self.config.super_include_file
1331
+ return [T['file_comment'].format(filename=fname)]
1332
+
1333
+ def _step_super_include_guard_start(self, step, ctx):
1334
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1335
+ return [T['guard_start'].rstrip('\n')]
1336
+
1337
+ def _step_super_include_guard_end(self, step, ctx):
1338
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1339
+ return [T['guard_end']]
1340
+
1341
+ def _step_super_include_common(self, step, ctx):
1342
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1343
+ layers = ctx['layers']
1344
+ structure = self.config.folder_structure
1345
+ parts = [T['common_section']]
1346
+ if structure == 'by_layer':
1347
+ parts.append('#include "statable_types_common.h"')
1348
+ for layer_name, sm in layers:
1349
+ layer = self._get_layer_name(sm)
1350
+ if layer:
1351
+ parts.append(
1352
+ f'#include "{layer}/'
1353
+ f'statable_types_{layer}.h"'
1354
+ )
1355
+ else:
1356
+ parts.append('#include "statable_types_common.h"')
1357
+ parts.append('#include "statable_types.h"')
1358
+ return parts
1359
+
1360
+ def _step_super_include_layer(self, step, ctx):
1361
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1362
+ layers = ctx['layers']
1363
+ structure = self.config.folder_structure
1364
+ parts = [T['layer_section']]
1365
+ if structure == 'by_layer':
1366
+ for layer_name, sm in layers:
1367
+ layer = self._get_layer_name(sm)
1368
+ if layer:
1369
+ parts.append(
1370
+ f'#include "{layer}/'
1371
+ f'statable_transitions_{layer}.h"'
1372
+ )
1373
+ parts.append(
1374
+ f'#include "{layer}/'
1375
+ f'statable_role_functions_{layer}.h"'
1376
+ )
1377
+ parts.append(
1378
+ f'#include "{layer}/'
1379
+ f'statable_state_actions_{layer}.h"'
1380
+ )
1381
+ else:
1382
+ parts.append('#include "statable_transitions.h"')
1383
+ parts.append('#include "statable_role_functions.h"')
1384
+ parts.append('#include "statable_state_actions.h"')
1385
+ return parts
1386
+
1387
+ def _step_super_include_project(self, step, ctx):
1388
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1389
+ return [
1390
+ T['project_section'],
1391
+ '#include "osal.h"',
1392
+ ]
1393
+
1394
+ def _step_super_include_extern_vars(self, step, ctx):
1395
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1396
+ layers = ctx['layers']
1397
+ parts = [T['extern_var_section'], T['extern_context']]
1398
+ for layer_name, sm in layers:
1399
+ layer = self._get_layer_name(sm)
1400
+ if layer:
1401
+ parts.append(T['extern_state'].format(layer=layer))
1402
+ else:
1403
+ parts.append(T['extern_state_nolayer'])
1404
+ return parts
1405
+
1406
+ def _step_super_include_extern_funcs(self, step, ctx):
1407
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1408
+ project = self.config.project_name
1409
+ parts = [
1410
+ T['extern_func_section'],
1411
+ T['extern_init'].format(project_name=project),
1412
+ T['extern_run'].format(project_name=project),
1413
+ ]
1414
+ gd = ctx['global_defs']
1415
+ interrupts = getattr(gd, 'interrupts', []) or []
1416
+ if interrupts:
1417
+ parts.append("")
1418
+ parts.append("/* ---- Interrupt handlers (extern) ---- */")
1419
+ for handler in interrupts:
1420
+ try:
1421
+ isr_name = self.interrupt_gen._get_isr_function_name(handler)
1422
+ except Exception:
1423
+ name = getattr(handler, 'name', '') or ''
1424
+ if not name:
1425
+ continue
1426
+ isr_name = f"ISR_{self.naming.to_pascal_case(name)}"
1427
+ parts.append(f"void {isr_name}(void);")
1428
+ return parts
1429
+
1430
+ def _step_super_include_external(self, step, ctx):
1431
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1432
+ result = [T['external_section']]
1433
+ for inc in self.config.external_includes:
1434
+ inc = inc.strip()
1435
+ if not inc:
1436
+ continue
1437
+ if inc.startswith('#include'):
1438
+ result.append(inc)
1439
+ else:
1440
+ result.append(f'#include "{inc}"')
1441
+ return result
1442
+
1443
+ def _step_super_include_user(self, step, ctx):
1444
+ T = self.templates.SUPER_INCLUDE_TEMPLATES
1445
+ return [
1446
+ T['user_section'],
1447
+ T['user_marker_start'],
1448
+ T['user_marker_end'],
1449
+ ]
1450
+
1451
+ def _step_super_loop_header(self, step, ctx):
1452
+ T = self.templates.SUPER_LOOP_TEMPLATES
1453
+ project = self.config.project_name
1454
+ return [T['file_comment'].format(project_name=project)]
1455
+
1456
+ def _step_super_loop_include(self, step, ctx):
1457
+ return [self.templates.SUPER_LOOP_TEMPLATES['include']]
1458
+
1459
+ def _step_super_loop_context_var(self, step, ctx):
1460
+ return [self.templates.SUPER_LOOP_TEMPLATES['context_var']]
1461
+
1462
+ def _step_super_loop_state_var(self, step, ctx):
1463
+ layers = ctx['layers']
1464
+ T = self.templates.SUPER_LOOP_TEMPLATES
1465
+ parts = []
1466
+ for layer_name, sm in layers:
1467
+ layer = self._get_layer_name(sm)
1468
+ if layer:
1469
+ parts.append(T['state_var'].format(layer=layer))
1470
+ else:
1471
+ parts.append(T['state_var_nolayer'])
1472
+ return parts
1473
+
1474
+ def _step_super_loop_init_func(self, step, ctx):
1475
+ layers = ctx['layers']
1476
+ project = self.config.project_name
1477
+ T = self.templates.SUPER_LOOP_TEMPLATES
1478
+ parts = [
1479
+ T['init_func_comment'],
1480
+ T['init_func_signature'].format(project_name=project),
1481
+ T['init_func_open'],
1482
+ T['init_context'],
1483
+ ]
1484
+ for layer_name, sm in layers:
1485
+ layer = self._get_layer_name(sm)
1486
+ initial = self._get_initial_state(sm)
1487
+ if layer:
1488
+ parts.append(T['init_state'].format(
1489
+ layer=layer, initial=initial,
1490
+ ))
1491
+ else:
1492
+ parts.append(T['init_state_nolayer'].format(
1493
+ initial=initial,
1494
+ ))
1495
+ parts.append(T['init_func_close'])
1496
+ return parts
1497
+
1498
+ def _step_super_loop_run_func(self, step, ctx):
1499
+ layers = ctx['layers']
1500
+ project = self.config.project_name
1501
+ T = self.templates.SUPER_LOOP_TEMPLATES
1502
+ parts = [
1503
+ T['run_func_comment'],
1504
+ T['run_func_signature'].format(project_name=project),
1505
+ T['run_func_open'],
1506
+ T['run_while'],
1507
+ ]
1508
+ # [v2.7.0] State actions (Entry / Exit / Do) - Do at loop top
1509
+ parts.append(" /* [v2.7.0] State actions (Do) */")
1510
+ for layer_name, sm in layers:
1511
+ layer = self._get_layer_name(sm)
1512
+ if layer:
1513
+ parts.append(
1514
+ f" {layer}_Do(g_{layer}_state, &g_ctx);"
1515
+ )
1516
+ parts.append("")
1517
+
1518
+ for layer_name, sm in layers:
1519
+ layer = self._get_layer_name(sm)
1520
+ priority = getattr(sm, 'layer_priority', 5)
1521
+ if layer:
1522
+ block = T['run_block'].format(
1523
+ layer=layer,
1524
+ priority=priority,
1525
+ )
1526
+ else:
1527
+ block = T['run_block_nolayer']
1528
+ indented = ' ' + block.replace(
1529
+ '\n', '\n ')
1530
+ parts.append(indented)
1531
+ parts.append(T['run_while_close'])
1532
+ parts.append(T['run_func_close'])
1533
+ return parts
1534
+
1535
+ def _generate_types_common_header(self, sm, gd):
1536
+ return self._run_steps('statable_types_common.h', sm, gd)
1537
+
1538
+ def _generate_types_header(self, sm, gd):
1539
+ return self._run_steps('statable_types.h', sm, gd)
1540
+
1541
+ def _generate_transitions_header(self, sm, gd):
1542
+ return self._run_steps('statable_transitions.h', sm, gd)
1543
+
1544
+ def _generate_transitions_source(self, sm, gd):
1545
+ return self._run_steps('statable_transitions.c', sm, gd)
1546
+
1547
+ def _generate_role_functions_header(self, sm, gd):
1548
+ return self._run_steps('statable_role_functions.h', sm, gd)
1549
+
1550
+ def _generate_role_functions_source(self, sm, gd):
1551
+ return self._run_steps('statable_role_functions.c', sm, gd)
1552
+
1553
+ def _generate_init_source(self, sm, gd):
1554
+ return self._run_steps('statable_init.c', sm, gd)
1555
+
1556
+ def _generate_event_queue_source(self, sm, gd):
1557
+ return self._run_steps('statable_event_queue.c', sm, gd)
1558
+
1559
+ def _generate_interrupt_source(self, sm, gd):
1560
+ return self._run_steps('statable_interrupt.c', sm, gd)
1561
+
1562
+ def _generate_timer_source(self, sm, gd):
1563
+ return self._run_steps('statable_timer.c', sm, gd)
1564
+
1565
+ def _generate_osal_header(self, sm, gd):
1566
+ return self._run_steps('osal.h', sm, gd)
1567
+
1568
+ def _generate_osal_source(self, sm, gd):
1569
+ return self._run_steps('osal.c', sm, gd)
1570
+
1571
+ def _generate_super_include(self, sm, gd):
1572
+ return self._run_steps('statable_all.h', sm, gd)
1573
+
1574
+ def _generate_super_loop(self, sm, gd):
1575
+ return self._run_steps(self.super_loop_filename, sm, gd)
1576
+
1577
+ def generate_all(self, state_machine, global_defs,
1578
+ role_function_library=None):
1579
+ self._setup_layer_generators(state_machine)
1580
+ prev = self._current_role_function_library
1581
+ self._current_role_function_library = role_function_library
1582
+ try:
1583
+ generated_files = {}
1584
+ for filename in self.file_generators.keys():
1585
+ if (filename == 'statable_all.h'
1586
+ and not self.config.generate_super_include):
1587
+ continue
1588
+ method_name = self.FILE_DISPATCH.get(filename)
1589
+ if method_name is None:
1590
+ continue
1591
+ method = getattr(self, method_name, None)
1592
+ if method is None:
1593
+ continue
1594
+ generated_files[filename] = method(
1595
+ state_machine, global_defs
1596
+ )
1597
+ return generated_files
1598
+ finally:
1599
+ self._current_role_function_library = prev
1600
+
1601
+ def generate_file(self, filename, state_machine, global_defs,
1602
+ role_function_library=None):
1603
+ self._setup_layer_generators(state_machine)
1604
+ prev = self._current_role_function_library
1605
+ self._current_role_function_library = role_function_library
1606
+ try:
1607
+ if filename not in self.file_generators:
1608
+ raise ValueError(f"Unknown file: {filename}")
1609
+ return self._run_steps(
1610
+ filename, state_machine, global_defs
1611
+ )
1612
+ finally:
1613
+ self._current_role_function_library = prev
1614
+
1615
+ def generate_all_layers(self, layers, global_defs,
1616
+ role_function_library=None):
1617
+ norm_layers = self._normalize_layers(layers)
1618
+ if not norm_layers:
1619
+ return {}
1620
+ structure = self.config.folder_structure
1621
+ if structure == 'by_layer':
1622
+ return self._generate_all_by_layer(
1623
+ norm_layers, global_defs, role_function_library
1624
+ )
1625
+ primary_sm = norm_layers[0][1]
1626
+ self._setup_layer_generators(primary_sm)
1627
+ prev = self._current_role_function_library
1628
+ self._current_role_function_library = role_function_library
1629
+ try:
1630
+ generated_files = {}
1631
+ for filename in self.file_generators.keys():
1632
+ if (filename == 'statable_all.h'
1633
+ and not self.config.generate_super_include):
1634
+ continue
1635
+ generated_files[filename] = self._run_steps_multi(
1636
+ filename, norm_layers, global_defs
1637
+ )
1638
+ return generated_files
1639
+ finally:
1640
+ self._current_role_function_library = prev
1641
+
1642
+ def _generate_all_by_layer(self, layers, global_defs,
1643
+ role_function_library):
1644
+ self._log_debug(
1645
+ f"_generate_all_by_layer: {len(layers)} layers"
1646
+ )
1647
+ generated_files = {}
1648
+ prev = self._current_role_function_library
1649
+ self._current_role_function_library = role_function_library
1650
+ prev_layers = self._all_layers_for_includes
1651
+ self._all_layers_for_includes = list(layers)
1652
+ try:
1653
+ for layer_name, sm in layers:
1654
+ self._setup_layer_generators(sm)
1655
+ if not layer_name:
1656
+ for fname in self.LAYER_SPECIFIC_FILES:
1657
+ method_name = self.FILE_DISPATCH.get(fname)
1658
+ if method_name is None:
1659
+ continue
1660
+ method = getattr(self, method_name, None)
1661
+ if method is None:
1662
+ continue
1663
+ generated_files[fname] = method(
1664
+ sm, global_defs
1665
+ )
1666
+ continue
1667
+ for fname in self.LAYER_SPECIFIC_FILES:
1668
+ method_name = self.FILE_DISPATCH.get(fname)
1669
+ if method_name is None:
1670
+ continue
1671
+ method = getattr(self, method_name, None)
1672
+ if method is None:
1673
+ continue
1674
+ content = method(sm, global_defs)
1675
+ fname_with_layer = self._layer_filename(
1676
+ fname, layer_name
1677
+ )
1678
+ generated_files[
1679
+ f"{layer_name}/{fname_with_layer}"
1680
+ ] = content
1681
+ common_names = list(self.COMMON_FILES) + [
1682
+ self.super_loop_filename
1683
+ ]
1684
+ for fname in common_names:
1685
+ if (fname == 'statable_all.h'
1686
+ and not self.config.generate_super_include):
1687
+ continue
1688
+ content = self._run_steps_multi(
1689
+ fname, layers, global_defs
1690
+ )
1691
+ generated_files[fname] = content
1692
+ return generated_files
1693
+ finally:
1694
+ self._current_role_function_library = prev
1695
+ self._all_layers_for_includes = prev_layers
1696
+
1697
+ def save_generated_code(self, generated_files, output_dir,
1698
+ layer_name: str = ''):
1699
+ saved_files = []
1700
+ os.makedirs(output_dir, exist_ok=True)
1701
+ for filename, content in generated_files.items():
1702
+ rel_path = self._resolve_output_path(
1703
+ filename, layer_name
1704
+ )
1705
+ filepath = os.path.join(output_dir, rel_path)
1706
+ parent = os.path.dirname(filepath)
1707
+ if parent:
1708
+ os.makedirs(parent, exist_ok=True)
1709
+ with open(filepath, 'w', encoding='utf-8') as f:
1710
+ f.write(content)
1711
+ saved_files.append(filepath)
1712
+ return saved_files
1713
+
1714
+ def save_generated_code_with_merge(self, generated_files,
1715
+ output_dir,
1716
+ layer_name: str = ''):
1717
+ merged_files = self.merger.merge_all_files(
1718
+ generated_files, output_dir,
1719
+ path_resolver=self._resolve_output_path,
1720
+ layer_name=layer_name,
1721
+ )
1722
+ return self.save_generated_code(
1723
+ merged_files, output_dir, layer_name
1724
+ )
1725
+
1726
+ def get_merge_summary(self, generated_files, output_dir,
1727
+ layer_name: str = ''):
1728
+ summary = {}
1729
+ for filename, content in generated_files.items():
1730
+ rel_path = self._resolve_output_path(
1731
+ filename, layer_name
1732
+ )
1733
+ existing_path = os.path.join(output_dir, rel_path)
1734
+ if os.path.exists(existing_path):
1735
+ with open(existing_path, 'r',
1736
+ encoding='utf-8') as f:
1737
+ existing_content = f.read()
1738
+ summary[filename] = (
1739
+ self.merger.get_user_code_summary(
1740
+ existing_content
1741
+ )
1742
+ )
1743
+ else:
1744
+ summary[filename] = {
1745
+ 'file_user_code': 0,
1746
+ 'func_user_codes': 0,
1747
+ 'file_tail_user_code': 0,
1748
+ }
1749
+ return summary
1750
+
1751
+ def get_generated_file_list(self):
1752
+ return list(self.file_generators.keys())