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,299 @@
1
+ # codegen/timer_generator.py
2
+ """
3
+ Timer variable generation module (fully data-driven version)
4
+
5
+ Version: 2.2 (2026-09-20 / MISRA 10.4 fix)
6
+ - Fix (MISRA C:2012 Rule 10.4): the derived-timer update expression
7
+ ctx->data.<derived> = (uint8_t)(ctx->data.<base> / 10);
8
+ mixed an unsigned `uint32_t` (base) with a signed integer literal
9
+ `10` (multiplier). Both operands of `/` must have the same
10
+ essential type category. The multiplier is now emitted as an
11
+ unsigned literal `10U`, satisfying 10.4.
12
+
13
+ [v1.5 fix]
14
+ - generate_struct: TimerVariables_t removed
15
+ Timer variables are already expanded into SystemData_t via
16
+ add_timer_variables(), so the unused typedef is no longer emitted.
17
+ """
18
+
19
+ import sys
20
+ import os
21
+ import logging
22
+ from typing import Dict, Callable, List, Any
23
+
24
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
25
+
26
+ from statable.global_defs import TimerBaseDef, TimerDerivedDef, GlobalDefinitions
27
+
28
+ try:
29
+ from .type_mapper import CTypeMapper
30
+ from .naming_convention import CNamingConvention
31
+ from .code_templates import CodeTemplates
32
+ except ImportError:
33
+ from type_mapper import CTypeMapper
34
+ from naming_convention import CNamingConvention
35
+ from code_templates import CodeTemplates
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class TimerGenerator:
41
+ """Timer variable generation class (fully data-driven)"""
42
+
43
+ def __init__(self):
44
+ self.mapper = CTypeMapper()
45
+ self.naming = CNamingConvention()
46
+ self.templates = CodeTemplates()
47
+ self.strings = self.templates.STRINGS
48
+ self.formats = self.templates.FORMATS
49
+
50
+ self.timer_struct_steps = [
51
+ {'action': 'comment'},
52
+ {'action': 'struct_start'},
53
+ {'action': 'members'},
54
+ {'action': 'struct_end'},
55
+ ]
56
+
57
+ self.init_steps = [
58
+ {'action': 'comment'},
59
+ {'action': 'signature'},
60
+ {'action': 'open'},
61
+ {'action': 'null_check'},
62
+ {'action': 'entry_log'},
63
+ {'action': 'init_base_timers'},
64
+ {'action': 'init_derived_timers'},
65
+ {'action': 'exit_log'},
66
+ {'action': 'close'},
67
+ ]
68
+
69
+ self.update_steps = [
70
+ {'action': 'comment'},
71
+ {'action': 'signature'},
72
+ {'action': 'open'},
73
+ {'action': 'null_check'},
74
+ {'action': 'update_derived_timers'},
75
+ {'action': 'close'},
76
+ ]
77
+
78
+ self.struct_executors: Dict[str, Callable] = {
79
+ 'comment': self._execute_struct_comment,
80
+ 'struct_start': self._execute_struct_start,
81
+ 'members': self._execute_members,
82
+ 'struct_end': self._execute_struct_end,
83
+ }
84
+
85
+ self.init_executors: Dict[str, Callable] = {
86
+ 'comment': self._execute_init_comment,
87
+ 'signature': self._execute_init_signature,
88
+ 'open': self._execute_open,
89
+ 'null_check': self._execute_null_check,
90
+ 'entry_log': self._execute_entry_log,
91
+ 'init_base_timers': self._execute_init_base_timers,
92
+ 'init_derived_timers': self._execute_init_derived_timers,
93
+ 'exit_log': self._execute_exit_log,
94
+ 'close': self._execute_close,
95
+ }
96
+
97
+ self.update_executors: Dict[str, Callable] = {
98
+ 'comment': self._execute_update_comment,
99
+ 'signature': self._execute_update_signature,
100
+ 'open': self._execute_open,
101
+ 'null_check': self._execute_null_check,
102
+ 'update_derived_timers': self._execute_update_derived_timers,
103
+ 'close': self._execute_close,
104
+ }
105
+
106
+ def _log_debug(self, message, level='debug'):
107
+ log_func = getattr(logger, level, logger.debug)
108
+ log_func(message)
109
+
110
+ def _get_all_timers(self, global_defs):
111
+ timers = []
112
+ timer_base = getattr(global_defs, 'timer_base', None)
113
+ if timer_base:
114
+ timers.append(timer_base)
115
+ timers.extend(getattr(global_defs, 'extra_timers', []))
116
+ return timers
117
+
118
+ def _get_all_derived_timers(self, global_defs):
119
+ derived_timers = []
120
+ for timer in self._get_all_timers(global_defs):
121
+ derived_timers.extend(getattr(timer, 'derived', []))
122
+ return derived_timers
123
+
124
+ def _execute_struct_comment(self, step, context):
125
+ return ["/* Timer variable struct */", "/* Manages timer variables used across the system */"]
126
+
127
+ def _execute_struct_start(self, step, context):
128
+ return ["typedef struct {"]
129
+
130
+ def _execute_members(self, step, context):
131
+ global_defs = context.get('global_defs')
132
+ indent = self.strings['indent_1']
133
+ lines = []
134
+
135
+ for timer in self._get_all_timers(global_defs):
136
+ var_name = self.naming.sanitize_identifier(getattr(timer, 'variable_name', 'unknown'))
137
+ data_type = self.mapper.map_type(getattr(timer, 'data_type', 'uint32_t'))
138
+ unit = getattr(timer, 'unit', '')
139
+ comment = f"/* {unit} */" if unit else ""
140
+ lines.append(f"{indent}{data_type} {var_name}; {comment}".rstrip())
141
+
142
+ for derived in self._get_all_derived_timers(global_defs):
143
+ var_name = self.naming.sanitize_identifier(getattr(derived, 'variable_name', 'unknown'))
144
+ data_type = self.mapper.map_type(getattr(derived, 'data_type', 'uint8_t'))
145
+ period_name = getattr(derived, 'period_name', '')
146
+ comment = f"/* {period_name} */" if period_name else ""
147
+ lines.append(f"{indent}{data_type} {var_name}; {comment}".rstrip())
148
+
149
+ return lines
150
+
151
+ def _execute_struct_end(self, step, context):
152
+ return ["} TimerVariables_t;"]
153
+
154
+ def _execute_init_comment(self, step, context):
155
+ return [
156
+ "/**",
157
+ " * @brief Timer variable initialization",
158
+ " * @param ctx System context pointer",
159
+ " */",
160
+ ]
161
+
162
+ def _execute_init_signature(self, step, context):
163
+ return ["void Timer_Init(SystemContext_t *ctx)"]
164
+
165
+ def _execute_open(self, step, context):
166
+ return ["{"]
167
+
168
+ def _execute_close(self, step, context):
169
+ return ["}"]
170
+
171
+ def _execute_null_check(self, step, context):
172
+ indent = self.strings['indent_1']
173
+ return [
174
+ f"{indent}if (ctx == NULL) {{",
175
+ f"{indent}{indent}return;",
176
+ f"{indent}}}",
177
+ ]
178
+
179
+ def _execute_entry_log(self, step, context):
180
+ indent = self.strings['indent_1']
181
+ return [f"{indent}{self.strings['log_debug']}(\"Enter Timer_Init\");", ""]
182
+
183
+ def _execute_init_base_timers(self, step, context):
184
+ global_defs = context.get('global_defs')
185
+ indent = self.strings['indent_1']
186
+ lines = []
187
+
188
+ for timer in self._get_all_timers(global_defs):
189
+ var_name = self.naming.sanitize_identifier(getattr(timer, 'variable_name', 'unknown'))
190
+ lines.append(f"{indent}ctx->data.{var_name} = 0;")
191
+
192
+ return lines
193
+
194
+ def _execute_init_derived_timers(self, step, context):
195
+ global_defs = context.get('global_defs')
196
+ indent = self.strings['indent_1']
197
+ lines = []
198
+
199
+ for derived in self._get_all_derived_timers(global_defs):
200
+ var_name = self.naming.sanitize_identifier(getattr(derived, 'variable_name', 'unknown'))
201
+ lines.append(f"{indent}ctx->data.{var_name} = 0;")
202
+
203
+ return lines
204
+
205
+ def _execute_exit_log(self, step, context):
206
+ indent = self.strings['indent_1']
207
+ return ["", f"{indent}{self.strings['log_debug']}(\"Exit Timer_Init\");"]
208
+
209
+ def _execute_update_comment(self, step, context):
210
+ return [
211
+ "/**",
212
+ " * @brief Timer update processing",
213
+ " * @param ctx System context pointer",
214
+ " */",
215
+ ]
216
+
217
+ def _execute_update_signature(self, step, context):
218
+ return ["void Timer_Update(SystemContext_t *ctx)"]
219
+
220
+ def _execute_update_derived_timers(self, step, context):
221
+ """Generate derived-timer update expressions.
222
+
223
+ [v2.2 / MISRA 10.4 fix]
224
+ The multiplier is emitted as an unsigned literal (`10U`)
225
+ because the base timer variable is `uint32_t` and the `/`
226
+ operator requires both operands to have the same essential
227
+ type category.
228
+
229
+ Before: ctx->data.<d> = (uint8_t)(ctx->data.<b> / 10);
230
+ After: ctx->data.<d> = (uint8_t)(ctx->data.<b> / 10U);
231
+ """
232
+ global_defs = context.get('global_defs')
233
+ indent = self.strings['indent_1']
234
+ lines = []
235
+
236
+ for timer in self._get_all_timers(global_defs):
237
+ base_var = self.naming.sanitize_identifier(
238
+ getattr(timer, 'variable_name', 'unknown'))
239
+
240
+ for derived in getattr(timer, 'derived', []):
241
+ derived_var = self.naming.sanitize_identifier(
242
+ getattr(derived, 'variable_name', 'unknown'))
243
+ multiplier = getattr(derived, 'multiplier', 1)
244
+ data_type = self.mapper.map_type(
245
+ getattr(derived, 'data_type', 'uint8_t'))
246
+
247
+ if multiplier > 0:
248
+ # [MISRA 10.4] unsigned literal
249
+ lines.append(
250
+ f"{indent}ctx->data.{derived_var} = "
251
+ f"({data_type})(ctx->data.{base_var} / "
252
+ f"{multiplier}U);"
253
+ )
254
+
255
+ return lines
256
+
257
+ def generate_struct(self, global_defs: GlobalDefinitions) -> str:
258
+ """
259
+ Generate timer variable struct.
260
+
261
+ [v1.5 change]
262
+ Timer variables are already expanded into SystemData_t;
263
+ TimerVariables_t is no longer used (v1.5 sec 9.6 #83).
264
+ The function is retained for backward compatibility but
265
+ now returns an empty string.
266
+ """
267
+ self._log_debug(
268
+ "generate_struct: TimerVariables_t is unused, returning empty"
269
+ )
270
+ return ""
271
+
272
+ def generate_init_function(self, global_defs: GlobalDefinitions) -> str:
273
+ self._log_debug("Generating timer init function")
274
+ context = {'global_defs': global_defs}
275
+ lines = []
276
+ for step in self.init_steps:
277
+ executor = self.init_executors.get(step['action'])
278
+ if executor:
279
+ lines.extend(executor(step, context))
280
+ return '\n'.join(lines)
281
+
282
+ def generate_update_function(self, global_defs: GlobalDefinitions) -> str:
283
+ self._log_debug("Generating timer update function")
284
+ context = {'global_defs': global_defs}
285
+ lines = []
286
+ for step in self.update_steps:
287
+ executor = self.update_executors.get(step['action'])
288
+ if executor:
289
+ lines.extend(executor(step, context))
290
+ return '\n'.join(lines)
291
+
292
+ def generate_all(self, global_defs: GlobalDefinitions) -> str:
293
+ lines = []
294
+ lines.append(self.generate_struct(global_defs))
295
+ lines.append("")
296
+ lines.append(self.generate_init_function(global_defs))
297
+ lines.append("")
298
+ lines.append(self.generate_update_function(global_defs))
299
+ return '\n'.join(lines)