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,721 @@
|
|
|
1
|
+
# codegen/struct_generator.py
|
|
2
|
+
"""
|
|
3
|
+
C struct code generation module (multi-layer state machine support)
|
|
4
|
+
|
|
5
|
+
Generated structs:
|
|
6
|
+
1. custom_type : user-defined types (unchanged)
|
|
7
|
+
2. system_data : global variables (unchanged)
|
|
8
|
+
3. event_flags : event flags (unchanged)
|
|
9
|
+
4. system_context : SystemContext_t (pending_event added)
|
|
10
|
+
5. transition_context : common TransitionContext_t (new base type)
|
|
11
|
+
6. layer_transition_context : TransitionContext_<Layer>_t (new, per-layer)
|
|
12
|
+
7. pending_event_macros : FIRE_EVENT / MAX_CONSECUTIVE_PENDING_EVENTS macros (new)
|
|
13
|
+
|
|
14
|
+
Design policy:
|
|
15
|
+
- Templates are data-tabled using string.Template
|
|
16
|
+
- pending_event is uint16_t (holds each layer's enum value generically)
|
|
17
|
+
- Provides both per-layer TransitionContext_<Layer>_t and
|
|
18
|
+
the common base type TransitionContext_t
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
import os
|
|
23
|
+
import logging
|
|
24
|
+
from string import Template
|
|
25
|
+
from typing import Dict, Callable, List, Any
|
|
26
|
+
|
|
27
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
28
|
+
|
|
29
|
+
from statable.global_defs import StructMemberDef, CustomTypeDef, GlobalDefinitions
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
from .type_mapper import CTypeMapper
|
|
33
|
+
from .naming_convention import CNamingConvention
|
|
34
|
+
from .code_templates import CodeTemplates
|
|
35
|
+
except ImportError:
|
|
36
|
+
from type_mapper import CTypeMapper
|
|
37
|
+
from naming_convention import CNamingConvention
|
|
38
|
+
from code_templates import CodeTemplates
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CStructGenerator:
|
|
44
|
+
"""C struct code generation class (multi-layer state machine support)"""
|
|
45
|
+
|
|
46
|
+
# ==================================================================
|
|
47
|
+
# [Data Table 1] Common struct templates (SystemContext_t etc.)
|
|
48
|
+
# ==================================================================
|
|
49
|
+
CONTEXT_TEMPLATES = {
|
|
50
|
+
# --- Section comment ---
|
|
51
|
+
'section_comment': Template(
|
|
52
|
+
'/* $title */\n'
|
|
53
|
+
'/* $description */\n'
|
|
54
|
+
),
|
|
55
|
+
|
|
56
|
+
# --- struct start ---
|
|
57
|
+
'struct_start': (
|
|
58
|
+
'typedef struct {\n'
|
|
59
|
+
),
|
|
60
|
+
|
|
61
|
+
# --- data / flags members ---
|
|
62
|
+
'member_data': Template(
|
|
63
|
+
' $system_data_type data; /* global variables */\n'
|
|
64
|
+
),
|
|
65
|
+
'member_flags': Template(
|
|
66
|
+
' $event_flags_type flags; /* event flags */\n'
|
|
67
|
+
),
|
|
68
|
+
|
|
69
|
+
# --- pending_event members ---
|
|
70
|
+
'member_pending_event': Template(
|
|
71
|
+
' uint16_t pending_event_$layer; /* [F-3 S2] pending event */\n'
|
|
72
|
+
),
|
|
73
|
+
'member_pending_event_valid': Template(
|
|
74
|
+
' bool pending_event_valid_$layer; /* [F-3 S2] pending valid flag */\n'
|
|
75
|
+
),
|
|
76
|
+
# [C-52] one queue per layer
|
|
77
|
+
'member_queue': Template(
|
|
78
|
+
' EventQueueState_t queue_$layer; /* [C-52] per-layer queue */\n'
|
|
79
|
+
),
|
|
80
|
+
|
|
81
|
+
# --- struct end ---
|
|
82
|
+
'struct_end': Template(
|
|
83
|
+
'} $type_name;\n'
|
|
84
|
+
),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# ==================================================================
|
|
88
|
+
# [Data Table 2] Common macro templates
|
|
89
|
+
# ==================================================================
|
|
90
|
+
MACRO_TEMPLATES = {
|
|
91
|
+
# --- Section comment ---
|
|
92
|
+
'section_comment': (
|
|
93
|
+
'\n'
|
|
94
|
+
'/* ============================================================== */\n'
|
|
95
|
+
'/* Pending event control */\n'
|
|
96
|
+
'/* ============================================================== */\n'
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
# --- FIRE_EVENT macro ---
|
|
100
|
+
'fire_event_macro': (
|
|
101
|
+
'\n'
|
|
102
|
+
'/* Event fire macro */\n'
|
|
103
|
+
'/* Used within role functions: FIRE_EVENT(ctx, EVENT_XXX_YYY); */\n'
|
|
104
|
+
'#define FIRE_EVENT(ctx, evt) do { \\\n'
|
|
105
|
+
' (ctx)->pending_event = (uint16_t)(evt); \\\n'
|
|
106
|
+
' (ctx)->pending_event_valid = true; \\\n'
|
|
107
|
+
'} while(0)\n'
|
|
108
|
+
),
|
|
109
|
+
|
|
110
|
+
# --- MAX_CONSECUTIVE_PENDING_EVENTS macro ---
|
|
111
|
+
'max_consecutive_macro': (
|
|
112
|
+
'\n'
|
|
113
|
+
'/* Upper limit for consecutive pending event processing */\n'
|
|
114
|
+
'/* Infinite loop prevention. Can be overridden at build time via -D */\n'
|
|
115
|
+
'#ifndef MAX_CONSECUTIVE_PENDING_EVENTS\n'
|
|
116
|
+
'#define MAX_CONSECUTIVE_PENDING_EVENTS 16\n'
|
|
117
|
+
'#endif\n'
|
|
118
|
+
),
|
|
119
|
+
# [C-52] per-layer queue size + init macro
|
|
120
|
+
'queue_section_comment': (
|
|
121
|
+
'\n'
|
|
122
|
+
'/* ============================================================== */\n'
|
|
123
|
+
'/* Layer event queue (C-52) */\n'
|
|
124
|
+
'/* ============================================================== */\n'
|
|
125
|
+
),
|
|
126
|
+
'queue_size_macro': (
|
|
127
|
+
'\n'
|
|
128
|
+
'/* Per-layer queue capacity. Override via -D at build time. */\n'
|
|
129
|
+
'#ifndef STATABLE_LAYER_QUEUE_SIZE\n'
|
|
130
|
+
'#define STATABLE_LAYER_QUEUE_SIZE 16\n'
|
|
131
|
+
'#endif\n'
|
|
132
|
+
),
|
|
133
|
+
'queue_state_type': (
|
|
134
|
+
'\n'
|
|
135
|
+
'/* Ring buffer state (one instance per layer, embedded in */\n'
|
|
136
|
+
'/* SystemContext_t). Protected by STATABLE_ENTER/EXIT_ */\n'
|
|
137
|
+
'/* CRITICAL hooks (see below). */\n'
|
|
138
|
+
'typedef struct {\n'
|
|
139
|
+
' uint16_t buffer[STATABLE_LAYER_QUEUE_SIZE];\n'
|
|
140
|
+
' volatile uint16_t head;\n'
|
|
141
|
+
' volatile uint16_t tail;\n'
|
|
142
|
+
' volatile uint16_t count;\n'
|
|
143
|
+
' volatile uint32_t dropped; /* [C-54] full-drop count */\n'
|
|
144
|
+
'} EventQueueState_t;\n'
|
|
145
|
+
),
|
|
146
|
+
# [C-54] Critical section hooks (default no-op)
|
|
147
|
+
'critical_hooks': (
|
|
148
|
+
'\n'
|
|
149
|
+
'/* ============================================================== */\n'
|
|
150
|
+
'/* Critical section hooks (C-54) */\n'
|
|
151
|
+
'/* ============================================================== */\n'
|
|
152
|
+
'/* Called around per-layer queue operations to make them safe */\n'
|
|
153
|
+
'/* against ISR preemption. Default: no-op (single-context). */\n'
|
|
154
|
+
'/* Override at build time via -D (see OSAL porting guide). */\n'
|
|
155
|
+
'#ifndef STATABLE_ENTER_CRITICAL\n'
|
|
156
|
+
'#define STATABLE_ENTER_CRITICAL() do { } while (0)\n'
|
|
157
|
+
'#endif\n'
|
|
158
|
+
'#ifndef STATABLE_EXIT_CRITICAL\n'
|
|
159
|
+
'#define STATABLE_EXIT_CRITICAL() do { } while (0)\n'
|
|
160
|
+
'#endif\n'
|
|
161
|
+
),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# ==================================================================
|
|
165
|
+
# [Data Table 3] Common TransitionContext_t template
|
|
166
|
+
# ==================================================================
|
|
167
|
+
COMMON_TRANSITION_CONTEXT_TEMPLATES = {
|
|
168
|
+
'comment': (
|
|
169
|
+
'/* Generic transition context (for common role functions across layers) */\n'
|
|
170
|
+
'/* Same layout as each layer\'s TransitionContext_<Layer>_t */\n'
|
|
171
|
+
),
|
|
172
|
+
'struct_start': (
|
|
173
|
+
'typedef struct {\n'
|
|
174
|
+
),
|
|
175
|
+
'member_from_state': (
|
|
176
|
+
' uint16_t from_state; /* source state (cast from layer enum) */\n'
|
|
177
|
+
),
|
|
178
|
+
'member_event': (
|
|
179
|
+
' uint16_t event; /* event (cast from layer enum) */\n'
|
|
180
|
+
),
|
|
181
|
+
'struct_end': (
|
|
182
|
+
'} TransitionContext_t;\n'
|
|
183
|
+
),
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# ==================================================================
|
|
187
|
+
# [Data Table 4] Per-layer TransitionContext_<Layer>_t
|
|
188
|
+
# ==================================================================
|
|
189
|
+
LAYER_TRANSITION_CONTEXT_TEMPLATES = {
|
|
190
|
+
'comment': Template(
|
|
191
|
+
'/* Transition context for $layer layer */\n'
|
|
192
|
+
),
|
|
193
|
+
'struct_start': (
|
|
194
|
+
'typedef struct {\n'
|
|
195
|
+
),
|
|
196
|
+
'member_from_state': Template(
|
|
197
|
+
' $state_type from_state; /* source state */\n'
|
|
198
|
+
),
|
|
199
|
+
'member_event': Template(
|
|
200
|
+
' $event_type event; /* event */\n'
|
|
201
|
+
),
|
|
202
|
+
'struct_end': Template(
|
|
203
|
+
'} $type_name;\n'
|
|
204
|
+
),
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# ==================================================================
|
|
208
|
+
# [Data Table 5] Member type detection
|
|
209
|
+
# ==================================================================
|
|
210
|
+
MEMBER_TYPE_DETECTORS = {
|
|
211
|
+
'bit_field': lambda m: getattr(m, 'bit_width', 0) > 0,
|
|
212
|
+
'array': lambda m: getattr(m, 'array_size', 0) > 0,
|
|
213
|
+
'normal': lambda m: True,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# ==================================================================
|
|
217
|
+
# [Data Table 6] Member generation templates
|
|
218
|
+
# ==================================================================
|
|
219
|
+
MEMBER_TEMPLATES = {
|
|
220
|
+
'bit_field': Template(
|
|
221
|
+
'$indent$c_type $name : $width;\n'
|
|
222
|
+
),
|
|
223
|
+
'array': Template(
|
|
224
|
+
'$indent$c_type $name[$size];\n'
|
|
225
|
+
),
|
|
226
|
+
'normal': Template(
|
|
227
|
+
'$indent$c_type $name;\n'
|
|
228
|
+
),
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# ==================================================================
|
|
232
|
+
# Constructor
|
|
233
|
+
# ==================================================================
|
|
234
|
+
def __init__(self):
|
|
235
|
+
self.mapper = CTypeMapper()
|
|
236
|
+
self.naming = CNamingConvention()
|
|
237
|
+
self.templates = CodeTemplates()
|
|
238
|
+
self.strings = self.templates.STRINGS
|
|
239
|
+
self.formats = self.templates.FORMATS
|
|
240
|
+
self.layer_name: str = ""
|
|
241
|
+
# [C-52] all layers of the current project
|
|
242
|
+
self._all_layers: list = []
|
|
243
|
+
|
|
244
|
+
def set_layer(self, layer_name: str):
|
|
245
|
+
"""Set layer name (for per-layer TransitionContext generation)"""
|
|
246
|
+
self.layer_name = layer_name
|
|
247
|
+
logger.debug(f"CStructGenerator.set_layer: layer_name='{layer_name}'")
|
|
248
|
+
|
|
249
|
+
def set_layers(self, layers):
|
|
250
|
+
"""[C-52] Record all (name, sm) pairs for queue members."""
|
|
251
|
+
self._all_layers = list(layers or [])
|
|
252
|
+
|
|
253
|
+
def _log_debug(self, message: str, level: str = 'debug'):
|
|
254
|
+
log_func = getattr(logger, level, logger.debug)
|
|
255
|
+
log_func(message)
|
|
256
|
+
|
|
257
|
+
# ==================================================================
|
|
258
|
+
# Helpers
|
|
259
|
+
# ==================================================================
|
|
260
|
+
def _detect_member_type(self, member) -> str:
|
|
261
|
+
for member_type, detector in self.MEMBER_TYPE_DETECTORS.items():
|
|
262
|
+
if detector(member):
|
|
263
|
+
return member_type
|
|
264
|
+
return 'normal'
|
|
265
|
+
|
|
266
|
+
def _generate_member(self, member) -> str:
|
|
267
|
+
"""Generate one member"""
|
|
268
|
+
member_type = self._detect_member_type(member)
|
|
269
|
+
template = self.MEMBER_TEMPLATES[member_type]
|
|
270
|
+
member_name = self.naming.sanitize_identifier(getattr(member, 'name', 'unnamed'))
|
|
271
|
+
c_type = self.mapper.map_type(getattr(member, 'data_type', 'void'))
|
|
272
|
+
indent = self.strings['indent_1']
|
|
273
|
+
|
|
274
|
+
if member_type == 'bit_field':
|
|
275
|
+
return template.substitute(
|
|
276
|
+
indent=indent, c_type=c_type,
|
|
277
|
+
name=member_name,
|
|
278
|
+
width=getattr(member, 'bit_width', 0),
|
|
279
|
+
)
|
|
280
|
+
elif member_type == 'array':
|
|
281
|
+
return template.substitute(
|
|
282
|
+
indent=indent, c_type=c_type,
|
|
283
|
+
name=member_name,
|
|
284
|
+
size=getattr(member, 'array_size', 0),
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
return template.substitute(
|
|
288
|
+
indent=indent, c_type=c_type, name=member_name,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# ==================================================================
|
|
292
|
+
# 1. Custom type
|
|
293
|
+
# ==================================================================
|
|
294
|
+
def _generate_custom_type(self, struct_def: CustomTypeDef) -> str:
|
|
295
|
+
self._log_debug(f"Generating custom type: {getattr(struct_def, 'name', 'unknown')}")
|
|
296
|
+
lines = []
|
|
297
|
+
|
|
298
|
+
# Comment
|
|
299
|
+
if getattr(struct_def, 'description', ''):
|
|
300
|
+
lines.append(f"/* {struct_def.description} */")
|
|
301
|
+
if getattr(struct_def, 'title', '') and struct_def.title != getattr(struct_def, 'name', ''):
|
|
302
|
+
lines.append(f"/* Title: {struct_def.title} */")
|
|
303
|
+
|
|
304
|
+
# struct body
|
|
305
|
+
lines.append("typedef struct {")
|
|
306
|
+
for member in getattr(struct_def, 'members', []):
|
|
307
|
+
if getattr(member, 'description', ''):
|
|
308
|
+
indent = self.strings['indent_1']
|
|
309
|
+
lines.append(f"{indent}/* {member.description} */")
|
|
310
|
+
if getattr(member, 'title', '') and member.title != getattr(member, 'name', ''):
|
|
311
|
+
indent = self.strings['indent_1']
|
|
312
|
+
lines.append(f"{indent}/* Title: {member.title} */")
|
|
313
|
+
lines.append(self._generate_member(member).rstrip('\n'))
|
|
314
|
+
|
|
315
|
+
type_name = self.naming.create_type_name(getattr(struct_def, 'name', 'Unknown'))
|
|
316
|
+
lines.append(f"}} {type_name};")
|
|
317
|
+
|
|
318
|
+
return '\n'.join(lines)
|
|
319
|
+
|
|
320
|
+
# ==================================================================
|
|
321
|
+
# 2. SystemData_t
|
|
322
|
+
# ==================================================================
|
|
323
|
+
def _generate_system_data(self, global_defs: GlobalDefinitions) -> str:
|
|
324
|
+
self._log_debug("Generating system data struct")
|
|
325
|
+
lines = []
|
|
326
|
+
|
|
327
|
+
# Comment
|
|
328
|
+
comment = self.templates.STRUCT_COMMENTS.get('system_data', {})
|
|
329
|
+
lines.append(f"/* {comment.get('title', '')} */")
|
|
330
|
+
lines.append(f"/* {comment.get('description', '')} */")
|
|
331
|
+
|
|
332
|
+
# struct body
|
|
333
|
+
lines.append("typedef struct {")
|
|
334
|
+
current_group = None
|
|
335
|
+
indent = self.strings['indent_1']
|
|
336
|
+
|
|
337
|
+
for var in getattr(global_defs, 'variables', []):
|
|
338
|
+
# Group comment
|
|
339
|
+
if getattr(var, 'group', '') and var.group != current_group:
|
|
340
|
+
if current_group is not None:
|
|
341
|
+
lines.append("")
|
|
342
|
+
lines.append(f"{indent}/* === {var.group} === */")
|
|
343
|
+
current_group = var.group
|
|
344
|
+
|
|
345
|
+
# Description / unit
|
|
346
|
+
comments = []
|
|
347
|
+
if getattr(var, 'description', ''):
|
|
348
|
+
comments.append(var.description)
|
|
349
|
+
if getattr(var, 'unit', ''):
|
|
350
|
+
comments.append(f"[{var.unit}]")
|
|
351
|
+
if comments:
|
|
352
|
+
lines.append(f"{indent}/* {' '.join(comments)} */")
|
|
353
|
+
|
|
354
|
+
# Variable body
|
|
355
|
+
var_name = self.naming.sanitize_identifier(getattr(var, 'name', 'unnamed'))
|
|
356
|
+
c_type = self.mapper.map_type(getattr(var, 'type', 'void'))
|
|
357
|
+
|
|
358
|
+
if getattr(var, 'array_size', 0) > 0:
|
|
359
|
+
lines.append(f"{indent}{c_type} {var_name}[{var.array_size}];")
|
|
360
|
+
else:
|
|
361
|
+
lines.append(f"{indent}{c_type} {var_name};")
|
|
362
|
+
|
|
363
|
+
lines.append(f"}} {self.templates.TYPE_NAMES['system_data']};")
|
|
364
|
+
return '\n'.join(lines)
|
|
365
|
+
|
|
366
|
+
# ==================================================================
|
|
367
|
+
# 3. EventFlags_t
|
|
368
|
+
# ==================================================================
|
|
369
|
+
def _generate_event_flags(self, global_defs: GlobalDefinitions) -> str:
|
|
370
|
+
self._log_debug("Generating event flags struct")
|
|
371
|
+
lines = []
|
|
372
|
+
|
|
373
|
+
# Comment
|
|
374
|
+
comment = self.templates.STRUCT_COMMENTS.get('event_flags', {})
|
|
375
|
+
lines.append(f"/* {comment.get('title', '')} */")
|
|
376
|
+
lines.append(f"/* {comment.get('description', '')} */")
|
|
377
|
+
|
|
378
|
+
# struct body
|
|
379
|
+
lines.append("typedef struct {")
|
|
380
|
+
current_group = None
|
|
381
|
+
indent = self.strings['indent_1']
|
|
382
|
+
|
|
383
|
+
for flag in getattr(global_defs, 'flags', []):
|
|
384
|
+
# Group comment
|
|
385
|
+
if getattr(flag, 'group', '') and flag.group != current_group:
|
|
386
|
+
if current_group is not None:
|
|
387
|
+
lines.append("")
|
|
388
|
+
lines.append(f"{indent}/* === {flag.group} === */")
|
|
389
|
+
current_group = flag.group
|
|
390
|
+
|
|
391
|
+
# Description
|
|
392
|
+
if getattr(flag, 'description', ''):
|
|
393
|
+
lines.append(f"{indent}/* {flag.description} */")
|
|
394
|
+
|
|
395
|
+
# Flag body
|
|
396
|
+
flag_name = self.naming.sanitize_identifier(getattr(flag, 'name', 'unnamed'))
|
|
397
|
+
lines.append(f"{indent}uint8_t {flag_name};")
|
|
398
|
+
|
|
399
|
+
lines.append(f"}} {self.templates.TYPE_NAMES['event_flags']};")
|
|
400
|
+
return '\n'.join(lines)
|
|
401
|
+
|
|
402
|
+
# ==================================================================
|
|
403
|
+
# 4. SystemContext_t (pending_event added)
|
|
404
|
+
# ==================================================================
|
|
405
|
+
def _generate_system_context(self, global_defs: GlobalDefinitions) -> str:
|
|
406
|
+
"""
|
|
407
|
+
Generate SystemContext_t (with pending_event / pending_event_valid).
|
|
408
|
+
|
|
409
|
+
Example output:
|
|
410
|
+
/* System context struct */
|
|
411
|
+
/* Integrates global variables and event flags */
|
|
412
|
+
typedef struct {
|
|
413
|
+
SystemData_t data; /* global variables */
|
|
414
|
+
EventFlags_t flags; /* event flags */
|
|
415
|
+
uint16_t pending_event; /* pending event */
|
|
416
|
+
bool pending_event_valid; /* pending event valid flag */
|
|
417
|
+
} SystemContext_t;
|
|
418
|
+
"""
|
|
419
|
+
self._log_debug("Generating system context struct (with pending_event)")
|
|
420
|
+
T = self.CONTEXT_TEMPLATES
|
|
421
|
+
lines = []
|
|
422
|
+
|
|
423
|
+
# Comment
|
|
424
|
+
comment = self.templates.STRUCT_COMMENTS.get('system_context', {})
|
|
425
|
+
lines.append(T['section_comment'].substitute(
|
|
426
|
+
title=comment.get('title', 'System context struct'),
|
|
427
|
+
description=comment.get('description', 'Integrates global variables and event flags'),
|
|
428
|
+
).rstrip('\n'))
|
|
429
|
+
|
|
430
|
+
# struct body
|
|
431
|
+
lines.append(T['struct_start'].rstrip('\n'))
|
|
432
|
+
lines.append(T['member_data'].substitute(
|
|
433
|
+
system_data_type=self.templates.TYPE_NAMES['system_data'],
|
|
434
|
+
).rstrip('\n'))
|
|
435
|
+
lines.append(T['member_flags'].substitute(
|
|
436
|
+
event_flags_type=self.templates.TYPE_NAMES['event_flags'],
|
|
437
|
+
).rstrip('\n'))
|
|
438
|
+
|
|
439
|
+
# [F-3 Step 2] per-layer pending_event slots
|
|
440
|
+
for layer_name, sm in self._all_layers:
|
|
441
|
+
layer = getattr(sm, 'layer_name', '') or layer_name
|
|
442
|
+
if layer:
|
|
443
|
+
lines.append(
|
|
444
|
+
T['member_pending_event'].substitute(
|
|
445
|
+
layer=layer).rstrip('\n'))
|
|
446
|
+
lines.append(
|
|
447
|
+
T['member_pending_event_valid'].substitute(
|
|
448
|
+
layer=layer).rstrip('\n'))
|
|
449
|
+
|
|
450
|
+
# [C-52] per-layer queues
|
|
451
|
+
for layer_name, sm in self._all_layers:
|
|
452
|
+
layer = getattr(sm, 'layer_name', '') or layer_name
|
|
453
|
+
if layer:
|
|
454
|
+
lines.append(
|
|
455
|
+
T['member_queue'].substitute(layer=layer).rstrip('\n'))
|
|
456
|
+
|
|
457
|
+
# struct end
|
|
458
|
+
lines.append(T['struct_end'].substitute(
|
|
459
|
+
type_name=self.templates.TYPE_NAMES['system_context'],
|
|
460
|
+
).rstrip('\n'))
|
|
461
|
+
|
|
462
|
+
return '\n'.join(lines)
|
|
463
|
+
|
|
464
|
+
# ==================================================================
|
|
465
|
+
# 5. Common TransitionContext_t (base type)
|
|
466
|
+
# ==================================================================
|
|
467
|
+
def generate_common_transition_context(self) -> str:
|
|
468
|
+
"""
|
|
469
|
+
Generate the common base type TransitionContext_t.
|
|
470
|
+
|
|
471
|
+
Example output:
|
|
472
|
+
/* Generic transition context (for common role functions across layers) */
|
|
473
|
+
/* Same layout as each layer's TransitionContext_<Layer>_t */
|
|
474
|
+
typedef struct {
|
|
475
|
+
uint16_t from_state; /* source state (cast from layer enum) */
|
|
476
|
+
uint16_t event; /* event (cast from layer enum) */
|
|
477
|
+
} TransitionContext_t;
|
|
478
|
+
"""
|
|
479
|
+
self._log_debug("Generating common TransitionContext_t")
|
|
480
|
+
T = self.COMMON_TRANSITION_CONTEXT_TEMPLATES
|
|
481
|
+
lines = [
|
|
482
|
+
T['comment'].rstrip('\n'),
|
|
483
|
+
T['struct_start'].rstrip('\n'),
|
|
484
|
+
T['member_from_state'].rstrip('\n'),
|
|
485
|
+
T['member_event'].rstrip('\n'),
|
|
486
|
+
T['struct_end'].rstrip('\n'),
|
|
487
|
+
]
|
|
488
|
+
return '\n'.join(lines)
|
|
489
|
+
|
|
490
|
+
# ==================================================================
|
|
491
|
+
# 6. Per-layer TransitionContext_<Layer>_t
|
|
492
|
+
# ==================================================================
|
|
493
|
+
def generate_layer_transition_context(self, state_type: str,
|
|
494
|
+
event_type: str) -> str:
|
|
495
|
+
"""
|
|
496
|
+
Generate per-layer TransitionContext_<Layer>_t.
|
|
497
|
+
|
|
498
|
+
Args:
|
|
499
|
+
state_type: layer state type (e.g. 'STATE_Driver_t')
|
|
500
|
+
event_type: layer event type (e.g. 'EVENT_Driver_t')
|
|
501
|
+
|
|
502
|
+
Example output:
|
|
503
|
+
/* Transition context for Driver layer */
|
|
504
|
+
typedef struct {
|
|
505
|
+
STATE_Driver_t from_state; /* source state */
|
|
506
|
+
EVENT_Driver_t event; /* event */
|
|
507
|
+
} TransitionContext_Driver_t;
|
|
508
|
+
"""
|
|
509
|
+
if not self.layer_name:
|
|
510
|
+
self._log_debug("generate_layer_transition_context: layer_name is empty, "
|
|
511
|
+
"returning empty string")
|
|
512
|
+
return ""
|
|
513
|
+
|
|
514
|
+
self._log_debug(f"Generating layer TransitionContext for '{self.layer_name}'")
|
|
515
|
+
T = self.LAYER_TRANSITION_CONTEXT_TEMPLATES
|
|
516
|
+
type_name = f"TransitionContext_{self.layer_name}_t"
|
|
517
|
+
|
|
518
|
+
lines = [
|
|
519
|
+
T['comment'].substitute(layer=self.layer_name).rstrip('\n'),
|
|
520
|
+
T['struct_start'].rstrip('\n'),
|
|
521
|
+
T['member_from_state'].substitute(state_type=state_type).rstrip('\n'),
|
|
522
|
+
T['member_event'].substitute(event_type=event_type).rstrip('\n'),
|
|
523
|
+
T['struct_end'].substitute(type_name=type_name).rstrip('\n'),
|
|
524
|
+
]
|
|
525
|
+
return '\n'.join(lines)
|
|
526
|
+
|
|
527
|
+
# ==================================================================
|
|
528
|
+
# 7. Pending event macros (FIRE_EVENT / MAX_CONSECUTIVE_PENDING_EVENTS)
|
|
529
|
+
# ==================================================================
|
|
530
|
+
def generate_pending_event_macros(self) -> str:
|
|
531
|
+
"""
|
|
532
|
+
Generate pending event control macros.
|
|
533
|
+
|
|
534
|
+
Example output:
|
|
535
|
+
/* ============================================================== */
|
|
536
|
+
/* Pending event control */
|
|
537
|
+
/* ============================================================== */
|
|
538
|
+
|
|
539
|
+
/* Event fire macro */
|
|
540
|
+
/* Used within role functions: FIRE_EVENT(ctx, EVENT_XXX_YYY); */
|
|
541
|
+
#define FIRE_EVENT(ctx, evt) do { \\
|
|
542
|
+
(ctx)->pending_event = (uint16_t)(evt); \\
|
|
543
|
+
(ctx)->pending_event_valid = true; \\
|
|
544
|
+
} while(0)
|
|
545
|
+
|
|
546
|
+
/* Upper limit for consecutive pending event processing */
|
|
547
|
+
/* Infinite loop prevention. Can be overridden at build time via -D */
|
|
548
|
+
#ifndef MAX_CONSECUTIVE_PENDING_EVENTS
|
|
549
|
+
#define MAX_CONSECUTIVE_PENDING_EVENTS 16
|
|
550
|
+
#endif
|
|
551
|
+
"""
|
|
552
|
+
self._log_debug("Generating pending event macros")
|
|
553
|
+
T = self.MACRO_TEMPLATES
|
|
554
|
+
parts = [T['section_comment']]
|
|
555
|
+
# [F-3 Step 2] FIRE_EVENT(ctx, evt) is removed.
|
|
556
|
+
parts.append(
|
|
557
|
+
'\n'
|
|
558
|
+
'/* [F-3 Step 2] FIRE_EVENT(ctx, evt) is removed. */\n'
|
|
559
|
+
'/* Use FIRE_EVENT_<Layer>(ctx, evt) with an explicit layer. */\n'
|
|
560
|
+
)
|
|
561
|
+
_bs = chr(92) # backslash
|
|
562
|
+
_tpl = (
|
|
563
|
+
'\n'
|
|
564
|
+
'/* Fire event for {layer} layer (protected) */\n'
|
|
565
|
+
'#define FIRE_EVENT_{layer}(ctx, evt) do {{ {bs}\n'
|
|
566
|
+
' STATABLE_ENTER_CRITICAL(); {bs}\n'
|
|
567
|
+
' (ctx)->pending_event_{layer} = (uint16_t)(evt); {bs}\n'
|
|
568
|
+
' (ctx)->pending_event_valid_{layer} = true; {bs}\n'
|
|
569
|
+
' STATABLE_EXIT_CRITICAL(); {bs}\n'
|
|
570
|
+
'}} while (0)\n'
|
|
571
|
+
)
|
|
572
|
+
seen = set()
|
|
573
|
+
for layer_name, sm in self._all_layers:
|
|
574
|
+
layer = getattr(sm, 'layer_name', '') or layer_name
|
|
575
|
+
if not layer or layer in seen:
|
|
576
|
+
continue
|
|
577
|
+
seen.add(layer)
|
|
578
|
+
parts.append(_tpl.format(layer=layer, bs=_bs))
|
|
579
|
+
parts.append(T['max_consecutive_macro'])
|
|
580
|
+
return ''.join(parts)
|
|
581
|
+
|
|
582
|
+
# ================================================================
|
|
583
|
+
# [C-52] Per-layer queue: types + macros
|
|
584
|
+
# ================================================================
|
|
585
|
+
def generate_layer_queue_types(self) -> str:
|
|
586
|
+
"""EventQueueState_t + size macro (emitted once)."""
|
|
587
|
+
if not self._all_layers:
|
|
588
|
+
return ""
|
|
589
|
+
T = self.MACRO_TEMPLATES
|
|
590
|
+
return ''.join([
|
|
591
|
+
T['queue_section_comment'],
|
|
592
|
+
T['critical_hooks'],
|
|
593
|
+
T['queue_size_macro'],
|
|
594
|
+
T['queue_state_type'],
|
|
595
|
+
])
|
|
596
|
+
|
|
597
|
+
def generate_layer_queue_macros(self) -> str:
|
|
598
|
+
"""FIRE_EVENT_QUEUE_<Layer> / INIT_EVENT_QUEUE_<Layer> macros."""
|
|
599
|
+
if not self._all_layers:
|
|
600
|
+
return ""
|
|
601
|
+
parts = []
|
|
602
|
+
seen = set()
|
|
603
|
+
for layer_name, sm in self._all_layers:
|
|
604
|
+
layer = getattr(sm, 'layer_name', '') or layer_name
|
|
605
|
+
if not layer or layer in seen:
|
|
606
|
+
continue
|
|
607
|
+
seen.add(layer)
|
|
608
|
+
parts.append(
|
|
609
|
+
f'\n'
|
|
610
|
+
f'/* Queue send (C-54: protected by hooks; drops+counts if full) */\n'
|
|
611
|
+
f'#define FIRE_EVENT_QUEUE_{layer}(ctx, evt) do {{ \\\n'
|
|
612
|
+
f' STATABLE_ENTER_CRITICAL(); \\\n'
|
|
613
|
+
f' EventQueueState_t *q_ = &(ctx)->queue_{layer}; \\\n'
|
|
614
|
+
f' if (q_->count < STATABLE_LAYER_QUEUE_SIZE) {{ \\\n'
|
|
615
|
+
f' q_->buffer[q_->tail] = (uint16_t)(evt); \\\n'
|
|
616
|
+
f' q_->tail = (uint16_t)((q_->tail + 1U) % STATABLE_LAYER_QUEUE_SIZE); \\\n'
|
|
617
|
+
f' q_->count++; \\\n'
|
|
618
|
+
f' }} else {{ \\\n'
|
|
619
|
+
f' q_->dropped++; \\\n'
|
|
620
|
+
f' }} \\\n'
|
|
621
|
+
f' STATABLE_EXIT_CRITICAL(); \\\n'
|
|
622
|
+
f'}} while (0)\n'
|
|
623
|
+
)
|
|
624
|
+
parts.append(
|
|
625
|
+
f'\n'
|
|
626
|
+
f'/* Queue init (C-54: protected) */\n'
|
|
627
|
+
f'#define INIT_EVENT_QUEUE_{layer}(ctx) do {{ \\\n'
|
|
628
|
+
f' STATABLE_ENTER_CRITICAL(); \\\n'
|
|
629
|
+
f' (ctx)->queue_{layer}.head = 0U; \\\n'
|
|
630
|
+
f' (ctx)->queue_{layer}.tail = 0U; \\\n'
|
|
631
|
+
f' (ctx)->queue_{layer}.count = 0U; \\\n'
|
|
632
|
+
f' (ctx)->queue_{layer}.dropped = 0U; \\\n'
|
|
633
|
+
f' STATABLE_EXIT_CRITICAL(); \\\n'
|
|
634
|
+
f'}} while (0)\n'
|
|
635
|
+
)
|
|
636
|
+
return ''.join(parts)
|
|
637
|
+
def generate_all_structs(self, global_defs: GlobalDefinitions) -> str:
|
|
638
|
+
"""Generate all structs in bulk
|
|
639
|
+
(custom_type + system_data + event_flags + system_context).
|
|
640
|
+
|
|
641
|
+
[v2.2.1 fix] Section header now emits a valid C block comment
|
|
642
|
+
using section_line_start / section_line_end.
|
|
643
|
+
"""
|
|
644
|
+
lines = []
|
|
645
|
+
|
|
646
|
+
def _section_header(title_key: str) -> str:
|
|
647
|
+
start = self.strings['section_line_start']
|
|
648
|
+
end = self.strings['section_line_end']
|
|
649
|
+
title = self.templates.SECTION_HEADERS.get(title_key, '')
|
|
650
|
+
return f"{start}\n * {title}\n{end}"
|
|
651
|
+
|
|
652
|
+
# custom_types
|
|
653
|
+
if getattr(global_defs, 'custom_types', []):
|
|
654
|
+
lines.append(_section_header('custom_types'))
|
|
655
|
+
lines.append("")
|
|
656
|
+
for custom_type in global_defs.custom_types:
|
|
657
|
+
lines.append(self._generate_custom_type(custom_type))
|
|
658
|
+
lines.append("")
|
|
659
|
+
|
|
660
|
+
# system_structs
|
|
661
|
+
lines.append(_section_header('system_structs'))
|
|
662
|
+
lines.append("")
|
|
663
|
+
lines.append(self._generate_system_data(global_defs))
|
|
664
|
+
lines.append("")
|
|
665
|
+
lines.append(self._generate_event_flags(global_defs))
|
|
666
|
+
lines.append("")
|
|
667
|
+
lines.append(self._generate_system_context(global_defs))
|
|
668
|
+
|
|
669
|
+
return '\n'.join(lines)
|
|
670
|
+
|
|
671
|
+
def generate_all(self, global_defs: GlobalDefinitions) -> Dict[str, str]:
|
|
672
|
+
"""
|
|
673
|
+
Return all generated artifacts as a dictionary.
|
|
674
|
+
|
|
675
|
+
Returns:
|
|
676
|
+
{
|
|
677
|
+
'custom_types': str, # user-defined types
|
|
678
|
+
'system_data': str, # SystemData_t
|
|
679
|
+
'event_flags': str, # EventFlags_t
|
|
680
|
+
'system_context': str, # SystemContext_t (with pending_event)
|
|
681
|
+
'common_transition_context': str, # common TransitionContext_t (base)
|
|
682
|
+
'pending_event_macros': str, # FIRE_EVENT / MAX_CONSECUTIVE
|
|
683
|
+
}
|
|
684
|
+
"""
|
|
685
|
+
self._log_debug("=== generate_all START (struct_generator) ===")
|
|
686
|
+
result = {
|
|
687
|
+
'custom_types': '\n\n'.join(
|
|
688
|
+
self._generate_custom_type(ct)
|
|
689
|
+
for ct in getattr(global_defs, 'custom_types', [])
|
|
690
|
+
),
|
|
691
|
+
'system_data': self._generate_system_data(global_defs),
|
|
692
|
+
'event_flags': self._generate_event_flags(global_defs),
|
|
693
|
+
'system_context': self._generate_system_context(global_defs),
|
|
694
|
+
'common_transition_context': self.generate_common_transition_context(),
|
|
695
|
+
'pending_event_macros': self.generate_pending_event_macros(),
|
|
696
|
+
}
|
|
697
|
+
self._log_debug("=== generate_all END (struct_generator) ===")
|
|
698
|
+
return result
|
|
699
|
+
|
|
700
|
+
# ==================================================================
|
|
701
|
+
# Backward-compatible API
|
|
702
|
+
# ==================================================================
|
|
703
|
+
def generate_struct(self, struct_type: str, item) -> str:
|
|
704
|
+
"""Backward compat: generate a single struct"""
|
|
705
|
+
if struct_type == 'custom_type':
|
|
706
|
+
return self._generate_custom_type(item)
|
|
707
|
+
elif struct_type == 'system_data':
|
|
708
|
+
return self._generate_system_data(item)
|
|
709
|
+
elif struct_type == 'event_flags':
|
|
710
|
+
return self._generate_event_flags(item)
|
|
711
|
+
elif struct_type == 'system_context':
|
|
712
|
+
return self._generate_system_context(item)
|
|
713
|
+
elif struct_type == 'common_transition_context':
|
|
714
|
+
return self.generate_common_transition_context()
|
|
715
|
+
elif struct_type == 'pending_event_macros':
|
|
716
|
+
return self.generate_pending_event_macros()
|
|
717
|
+
elif struct_type == 'layer_queue_types':
|
|
718
|
+
return self.generate_layer_queue_types()
|
|
719
|
+
elif struct_type == 'layer_queue_macros':
|
|
720
|
+
return self.generate_layer_queue_macros()
|
|
721
|
+
raise ValueError(f"Unknown struct type: {struct_type}")
|