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
statable/sample_data.py
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
"""Sample data generation (business logic layer).
|
|
2
|
+
|
|
3
|
+
Version History
|
|
4
|
+
---------------
|
|
5
|
+
v2.2 - entry / exit: List[str]; cell actions and relations added.
|
|
6
|
+
v3.8 - RoleFunction.used_global_vars / used_events / used_literals
|
|
7
|
+
are available (optional).
|
|
8
|
+
v3.12 - Explicit namespace="Application" added to all sample role
|
|
9
|
+
functions. Previously they all used namespace="", which
|
|
10
|
+
meant the namespace combo box in the GUI had no candidates
|
|
11
|
+
coming from the role function list.
|
|
12
|
+
|
|
13
|
+
Design note
|
|
14
|
+
-----------
|
|
15
|
+
In StaTable the role-function namespace must match the
|
|
16
|
+
*layer name* (tab name) for the declaration to be emitted
|
|
17
|
+
by codegen/role_function_generator.py. Feature-group style
|
|
18
|
+
namespaces ("Sensor", "Error", ...) are not supported by
|
|
19
|
+
the current layer model. See SPEC_OVERVIEW §3.2.7
|
|
20
|
+
"Design Principle (v2.3)".
|
|
21
|
+
|
|
22
|
+
The sample below therefore uses namespace="Application",
|
|
23
|
+
which matches the initial tab name created by MainWindow.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from .model import (
|
|
27
|
+
State, Event, Transition, StateType, EventKind,
|
|
28
|
+
RoleFunction, EventDeliveryType, EventSourceLayer,
|
|
29
|
+
ActionStep, TransitionRelation,
|
|
30
|
+
)
|
|
31
|
+
from .state_machine import StateMachine
|
|
32
|
+
from .global_defs import (
|
|
33
|
+
GlobalDefinitions, SystemVariable, EventFlag,
|
|
34
|
+
InterruptHandlerDef, InterruptAction,
|
|
35
|
+
DevicePlaceholderDef, TimerBaseDef, TimerDerivedDef,
|
|
36
|
+
EventQueueDef, CustomTypeDef, StructMemberDef,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# [v3.12] Layer name used by the sample state machine.
|
|
41
|
+
# Must match the initial tab name created by MainWindow.
|
|
42
|
+
_SAMPLE_LAYER = "Application"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def create_sample_state_machine() -> StateMachine:
|
|
46
|
+
sm = StateMachine()
|
|
47
|
+
sm.layer_name = _SAMPLE_LAYER
|
|
48
|
+
|
|
49
|
+
# ==================================================================
|
|
50
|
+
# States (v2.2: entry / exit are List[str])
|
|
51
|
+
# ==================================================================
|
|
52
|
+
sm.add_state(State("Idle",
|
|
53
|
+
entry=["Idle_Entry"],
|
|
54
|
+
description="Initial state"))
|
|
55
|
+
sm.add_state(State("Active",
|
|
56
|
+
do="Active_Do",
|
|
57
|
+
description="Running"))
|
|
58
|
+
sm.add_state(State("Error",
|
|
59
|
+
entry=["Error_Entry"],
|
|
60
|
+
exit=["Error_Exit"],
|
|
61
|
+
description="Error state"))
|
|
62
|
+
sm.add_state(State("Halt",
|
|
63
|
+
type=StateType.FINAL,
|
|
64
|
+
description="Stopped state"))
|
|
65
|
+
|
|
66
|
+
sm.set_initial("Idle")
|
|
67
|
+
|
|
68
|
+
# ==================================================================
|
|
69
|
+
# Events
|
|
70
|
+
# ==================================================================
|
|
71
|
+
sm.add_event(Event(name="START", id=1, description="Startup request",
|
|
72
|
+
delivery_type=EventDeliveryType.DIRECT,
|
|
73
|
+
source_layer=EventSourceLayer.MIDDLEWARE,
|
|
74
|
+
title="Startup request"))
|
|
75
|
+
sm.add_event(Event(name="STOP", id=2, description="Stop request",
|
|
76
|
+
delivery_type=EventDeliveryType.DIRECT,
|
|
77
|
+
source_layer=EventSourceLayer.MIDDLEWARE,
|
|
78
|
+
title="Stop request"))
|
|
79
|
+
sm.add_event(Event(name="ERROR", id=3, description="Error notification",
|
|
80
|
+
delivery_type=EventDeliveryType.QUEUE,
|
|
81
|
+
source_layer=EventSourceLayer.DRIVER,
|
|
82
|
+
data_type="uint8_t", data_name="err_code",
|
|
83
|
+
title="Error notification"))
|
|
84
|
+
sm.add_event(Event(name="TIMER0_OVERFLOW", id=4,
|
|
85
|
+
description="1ms timer expired",
|
|
86
|
+
delivery_type=EventDeliveryType.DOUBLE,
|
|
87
|
+
source_layer=EventSourceLayer.DRIVER,
|
|
88
|
+
title="Timer expired"))
|
|
89
|
+
sm.add_event(Event(name="", id=0, kind=EventKind.SIGNAL,
|
|
90
|
+
description="Completion transition",
|
|
91
|
+
delivery_type=EventDeliveryType.DIRECT,
|
|
92
|
+
source_layer=EventSourceLayer.MIDDLEWARE,
|
|
93
|
+
title="Completion transition"))
|
|
94
|
+
|
|
95
|
+
# ==================================================================
|
|
96
|
+
# Role functions
|
|
97
|
+
#
|
|
98
|
+
# [v3.12] All sample functions belong to the Application layer.
|
|
99
|
+
# namespace is now explicitly set so that the namespace
|
|
100
|
+
# combo box in RoleFunctionDialog / SettingsPanel has
|
|
101
|
+
# at least one candidate even before the user adds
|
|
102
|
+
# additional tabs.
|
|
103
|
+
#
|
|
104
|
+
# Call sites (pre_actions / entry / exit / ActionStep.
|
|
105
|
+
# role_function) keep using the bare names below; the
|
|
106
|
+
# code generator resolves them via the bare-name
|
|
107
|
+
# fallback in _get_call_sites_for_func().
|
|
108
|
+
# ==================================================================
|
|
109
|
+
sm.add_role_function(RoleFunction(
|
|
110
|
+
name="Sensor_Init",
|
|
111
|
+
namespace=_SAMPLE_LAYER,
|
|
112
|
+
description="Sensor initialization",
|
|
113
|
+
return_type="int",
|
|
114
|
+
arg1_type="uint8_t", arg1_name="channel",
|
|
115
|
+
arg2_type="uint32_t", arg2_name="timeout_ms",
|
|
116
|
+
title="Sensor initialization",
|
|
117
|
+
))
|
|
118
|
+
sm.add_role_function(RoleFunction(
|
|
119
|
+
name="Error_Log",
|
|
120
|
+
namespace=_SAMPLE_LAYER,
|
|
121
|
+
description="Error log output",
|
|
122
|
+
return_type="void",
|
|
123
|
+
arg1_type="int", arg1_name="err_code",
|
|
124
|
+
arg2_type="int", arg2_name="level",
|
|
125
|
+
title="Error log output",
|
|
126
|
+
))
|
|
127
|
+
sm.add_role_function(RoleFunction(
|
|
128
|
+
name="Start_Init",
|
|
129
|
+
namespace=_SAMPLE_LAYER,
|
|
130
|
+
description="Startup initialization",
|
|
131
|
+
return_type="void",
|
|
132
|
+
title="Startup initialization",
|
|
133
|
+
))
|
|
134
|
+
sm.add_role_function(RoleFunction(
|
|
135
|
+
name="Stop_Cleanup",
|
|
136
|
+
namespace=_SAMPLE_LAYER,
|
|
137
|
+
description="Stop cleanup",
|
|
138
|
+
return_type="void",
|
|
139
|
+
title="Stop cleanup",
|
|
140
|
+
))
|
|
141
|
+
sm.add_role_function(RoleFunction(
|
|
142
|
+
name="Retry_Increment",
|
|
143
|
+
namespace=_SAMPLE_LAYER,
|
|
144
|
+
description="Increment retry counter",
|
|
145
|
+
return_type="void",
|
|
146
|
+
title="Increment retry counter",
|
|
147
|
+
))
|
|
148
|
+
sm.add_role_function(RoleFunction(
|
|
149
|
+
name="Ignore_Event",
|
|
150
|
+
namespace=_SAMPLE_LAYER,
|
|
151
|
+
description="Ignore the current event",
|
|
152
|
+
return_type="void",
|
|
153
|
+
title="Ignore the current event",
|
|
154
|
+
))
|
|
155
|
+
sm.add_role_function(RoleFunction(
|
|
156
|
+
name="Idle_Entry",
|
|
157
|
+
namespace=_SAMPLE_LAYER,
|
|
158
|
+
description="Idle state entry action",
|
|
159
|
+
return_type="void",
|
|
160
|
+
title="Idle entry",
|
|
161
|
+
))
|
|
162
|
+
sm.add_role_function(RoleFunction(
|
|
163
|
+
name="Error_Entry",
|
|
164
|
+
namespace=_SAMPLE_LAYER,
|
|
165
|
+
description="Error state entry action",
|
|
166
|
+
return_type="void",
|
|
167
|
+
title="Error entry",
|
|
168
|
+
))
|
|
169
|
+
sm.add_role_function(RoleFunction(
|
|
170
|
+
name="Error_Exit",
|
|
171
|
+
namespace=_SAMPLE_LAYER,
|
|
172
|
+
description="Error state exit action",
|
|
173
|
+
return_type="void",
|
|
174
|
+
title="Error exit",
|
|
175
|
+
))
|
|
176
|
+
|
|
177
|
+
# ==================================================================
|
|
178
|
+
# Transitions
|
|
179
|
+
# ==================================================================
|
|
180
|
+
|
|
181
|
+
# Idle --START--> Active
|
|
182
|
+
sm.add_transition(Transition(
|
|
183
|
+
source="Idle",
|
|
184
|
+
event="START",
|
|
185
|
+
condition="",
|
|
186
|
+
pre_actions=["Start_Init"],
|
|
187
|
+
target="Active",
|
|
188
|
+
has_else=False,
|
|
189
|
+
early_return=True,
|
|
190
|
+
label="T1",
|
|
191
|
+
title="Startup",
|
|
192
|
+
))
|
|
193
|
+
|
|
194
|
+
# Active --STOP--> Idle
|
|
195
|
+
sm.add_transition(Transition(
|
|
196
|
+
source="Active",
|
|
197
|
+
event="STOP",
|
|
198
|
+
condition="",
|
|
199
|
+
pre_actions=["Stop_Cleanup"],
|
|
200
|
+
target="Idle",
|
|
201
|
+
has_else=False,
|
|
202
|
+
early_return=True,
|
|
203
|
+
label="T1",
|
|
204
|
+
title="Stop",
|
|
205
|
+
))
|
|
206
|
+
|
|
207
|
+
# Active --ERROR--> Error (err_code != 0)
|
|
208
|
+
sm.add_transition(Transition(
|
|
209
|
+
source="Active",
|
|
210
|
+
event="ERROR",
|
|
211
|
+
condition="err_code != 0",
|
|
212
|
+
pre_actions=["Error_Log"],
|
|
213
|
+
target="Error",
|
|
214
|
+
has_else=False,
|
|
215
|
+
early_return=True,
|
|
216
|
+
label="T1",
|
|
217
|
+
title="To error",
|
|
218
|
+
))
|
|
219
|
+
|
|
220
|
+
# Active --ERROR--> Active (err_code == 0) [Tentative]
|
|
221
|
+
sm.add_transition(Transition(
|
|
222
|
+
source="Active",
|
|
223
|
+
event="ERROR",
|
|
224
|
+
condition="err_code == 0",
|
|
225
|
+
pre_actions=["Ignore_Event"],
|
|
226
|
+
target="Active",
|
|
227
|
+
has_else=False,
|
|
228
|
+
early_return=False,
|
|
229
|
+
label="T2",
|
|
230
|
+
title="Ignore",
|
|
231
|
+
))
|
|
232
|
+
|
|
233
|
+
# Error --(Completion)--> Active (retry)
|
|
234
|
+
sm.add_transition(Transition(
|
|
235
|
+
source="Error",
|
|
236
|
+
event="",
|
|
237
|
+
condition="retry_count < 3",
|
|
238
|
+
pre_actions=["Retry_Increment"],
|
|
239
|
+
target="Active",
|
|
240
|
+
has_else=False,
|
|
241
|
+
early_return=True,
|
|
242
|
+
label="T1",
|
|
243
|
+
title="Retry",
|
|
244
|
+
))
|
|
245
|
+
|
|
246
|
+
# Error --(Completion)--> Halt (give up)
|
|
247
|
+
sm.add_transition(Transition(
|
|
248
|
+
source="Error",
|
|
249
|
+
event="",
|
|
250
|
+
condition="retry_count >= 3",
|
|
251
|
+
pre_actions=[],
|
|
252
|
+
target="Halt",
|
|
253
|
+
has_else=False,
|
|
254
|
+
early_return=True,
|
|
255
|
+
label="T2",
|
|
256
|
+
title="Give up",
|
|
257
|
+
))
|
|
258
|
+
|
|
259
|
+
# ==================================================================
|
|
260
|
+
# Cell actions (v2.2)
|
|
261
|
+
# ==================================================================
|
|
262
|
+
|
|
263
|
+
# Idle + START: sensor init before condition check
|
|
264
|
+
sm.set_actions_for_cell("Idle", "START", [
|
|
265
|
+
ActionStep(role_function="Sensor_Init",
|
|
266
|
+
trigger="before_transitions",
|
|
267
|
+
title="Initialize sensor on startup"),
|
|
268
|
+
])
|
|
269
|
+
|
|
270
|
+
# Active + ERROR: log before and after
|
|
271
|
+
sm.set_actions_for_cell("Active", "ERROR", [
|
|
272
|
+
ActionStep(role_function="Error_Log",
|
|
273
|
+
trigger="before_transitions",
|
|
274
|
+
title="Log before evaluating error"),
|
|
275
|
+
ActionStep(role_function="Error_Log",
|
|
276
|
+
trigger="after_transitions",
|
|
277
|
+
title="Log after evaluating error"),
|
|
278
|
+
])
|
|
279
|
+
|
|
280
|
+
# Error + (Completion): log before retry evaluation
|
|
281
|
+
sm.set_actions_for_cell("Error", "", [
|
|
282
|
+
ActionStep(role_function="Error_Log",
|
|
283
|
+
trigger="before_transitions",
|
|
284
|
+
title="Log before retry evaluation"),
|
|
285
|
+
])
|
|
286
|
+
|
|
287
|
+
# Active + STOP: cleanup after stop
|
|
288
|
+
sm.set_actions_for_cell("Active", "STOP", [
|
|
289
|
+
ActionStep(role_function="Stop_Cleanup",
|
|
290
|
+
trigger="after_transitions",
|
|
291
|
+
title="Cleanup after stop"),
|
|
292
|
+
])
|
|
293
|
+
|
|
294
|
+
# ==================================================================
|
|
295
|
+
# Cell relations (v2.2)
|
|
296
|
+
# ==================================================================
|
|
297
|
+
|
|
298
|
+
# Active + ERROR: T1 and T2 evaluated sequentially
|
|
299
|
+
sm.set_relations_for_cell("Active", "ERROR", [
|
|
300
|
+
TransitionRelation(
|
|
301
|
+
kind="sequential",
|
|
302
|
+
members=["T1", "T2"],
|
|
303
|
+
shared_condition="",
|
|
304
|
+
note="T1 (error) first, then T2 (ignore)",
|
|
305
|
+
),
|
|
306
|
+
])
|
|
307
|
+
|
|
308
|
+
# Error + (Completion): T1 and T2 mutually exclusive
|
|
309
|
+
sm.set_relations_for_cell("Error", "", [
|
|
310
|
+
TransitionRelation(
|
|
311
|
+
kind="exclusive",
|
|
312
|
+
members=["T1", "T2"],
|
|
313
|
+
shared_condition="",
|
|
314
|
+
note="At most one fires (retry or halt)",
|
|
315
|
+
),
|
|
316
|
+
])
|
|
317
|
+
|
|
318
|
+
return sm
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def create_sample_global_defs() -> GlobalDefinitions:
|
|
322
|
+
defs = GlobalDefinitions()
|
|
323
|
+
|
|
324
|
+
defs.custom_types.append(CustomTypeDef(
|
|
325
|
+
name="SystemStatus_t",
|
|
326
|
+
description="System status struct",
|
|
327
|
+
title="System status",
|
|
328
|
+
members=[
|
|
329
|
+
StructMemberDef(name="power_on", data_type="uint8_t", bit_width=1,
|
|
330
|
+
description="Power ON flag", title="Power ON"),
|
|
331
|
+
StructMemberDef(name="mode", data_type="uint8_t", bit_width=3,
|
|
332
|
+
description="Mode", title="Mode"),
|
|
333
|
+
StructMemberDef(name="status", data_type="uint8_t", bit_width=4,
|
|
334
|
+
description="Status", title="Status"),
|
|
335
|
+
]
|
|
336
|
+
))
|
|
337
|
+
|
|
338
|
+
defs.custom_types.append(CustomTypeDef(
|
|
339
|
+
name="DataPacket_t",
|
|
340
|
+
description="Data packet struct",
|
|
341
|
+
title="Data packet",
|
|
342
|
+
members=[
|
|
343
|
+
StructMemberDef(name="data", data_type="uint8_t",
|
|
344
|
+
description="Data array", title="Data array",
|
|
345
|
+
array_size=64),
|
|
346
|
+
StructMemberDef(name="length", data_type="uint16_t",
|
|
347
|
+
description="Data length", title="Data length"),
|
|
348
|
+
]
|
|
349
|
+
))
|
|
350
|
+
|
|
351
|
+
defs.variables.append(SystemVariable(
|
|
352
|
+
name="battery_voltage", type="uint16_t", unit="mV",
|
|
353
|
+
default_value="0", group="Power",
|
|
354
|
+
description="Battery voltage", title="Battery voltage"))
|
|
355
|
+
defs.variables.append(SystemVariable(
|
|
356
|
+
name="payload", type="uint8_t", unit="bytes",
|
|
357
|
+
default_value="", group="Communication",
|
|
358
|
+
description="Receive data buffer", title="Receive buffer",
|
|
359
|
+
array_size=64))
|
|
360
|
+
defs.variables.append(SystemVariable(
|
|
361
|
+
name="system_status", type="SystemStatus_t",
|
|
362
|
+
group="System", description="System status",
|
|
363
|
+
title="System status"))
|
|
364
|
+
defs.variables.append(SystemVariable(
|
|
365
|
+
name="retry_count", type="uint8_t", unit="",
|
|
366
|
+
default_value="0", group="Error",
|
|
367
|
+
description="Retry counter", title="Retry counter"))
|
|
368
|
+
|
|
369
|
+
defs.flags.append(EventFlag(
|
|
370
|
+
name="EVT_START_REQ", min_value=0, max_value=1,
|
|
371
|
+
group="SystemEvents", description="Startup request",
|
|
372
|
+
title="Startup request flag"))
|
|
373
|
+
defs.flags.append(EventFlag(
|
|
374
|
+
name="EVT_MODE", min_value=0, max_value=3,
|
|
375
|
+
group="SystemEvents", description="Mode indication",
|
|
376
|
+
title="Mode indication flag"))
|
|
377
|
+
|
|
378
|
+
defs.interrupts.append(InterruptHandlerDef(
|
|
379
|
+
name="TIMER0", description="1ms period timer",
|
|
380
|
+
event_names=["TIMER0_OVERFLOW"], is_timer=True,
|
|
381
|
+
actions=[
|
|
382
|
+
InterruptAction(condition="g_tick_100ms >= 5",
|
|
383
|
+
action="StateMachine_EnqueueEvent(EVENT_TICK);"),
|
|
384
|
+
InterruptAction(condition="",
|
|
385
|
+
action="g_system_tick++;\nUpdateDerivedTimers();"),
|
|
386
|
+
],
|
|
387
|
+
title="Timer0Interrupt"
|
|
388
|
+
))
|
|
389
|
+
|
|
390
|
+
defs.placeholders.append(DevicePlaceholderDef(
|
|
391
|
+
name="TIMER0_IRQ_FLAG",
|
|
392
|
+
description="Timer0 interrupt flag clear register",
|
|
393
|
+
title="Timer0 IRQFlag"))
|
|
394
|
+
|
|
395
|
+
defs.timer_base = TimerBaseDef(
|
|
396
|
+
variable_name="g_system_tick", unit="1ms",
|
|
397
|
+
data_type="volatile uint32_t",
|
|
398
|
+
derived=[
|
|
399
|
+
TimerDerivedDef(period_name="10ms", multiplier=10,
|
|
400
|
+
variable_name="g_tick_10ms",
|
|
401
|
+
data_type="uint8_t", title="10msTimer"),
|
|
402
|
+
TimerDerivedDef(period_name="100ms", multiplier=100,
|
|
403
|
+
variable_name="g_tick_100ms",
|
|
404
|
+
data_type="uint8_t", title="100msTimer"),
|
|
405
|
+
TimerDerivedDef(period_name="1s", multiplier=1000,
|
|
406
|
+
variable_name="g_tick_1s",
|
|
407
|
+
data_type="uint16_t", title="1sTimer"),
|
|
408
|
+
],
|
|
409
|
+
title="System timer base",
|
|
410
|
+
interrupt_name="TIMER0"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
defs.extra_timers.append(TimerBaseDef(
|
|
414
|
+
variable_name="g_high_speed_tick", unit="100us",
|
|
415
|
+
data_type="volatile uint32_t",
|
|
416
|
+
derived=[
|
|
417
|
+
TimerDerivedDef(period_name="1ms", multiplier=10,
|
|
418
|
+
variable_name="g_hs_1ms",
|
|
419
|
+
data_type="uint16_t",
|
|
420
|
+
title="High-speed 1ms timer"),
|
|
421
|
+
],
|
|
422
|
+
title="High-speed timer base",
|
|
423
|
+
interrupt_name="TIMER1"
|
|
424
|
+
))
|
|
425
|
+
|
|
426
|
+
defs.event_queues.append(EventQueueDef(
|
|
427
|
+
name="UartQueue", size=16, element_type="uint8_t",
|
|
428
|
+
event_ids=["ERROR"], priority_enabled=False,
|
|
429
|
+
interrupt_safe=True, rtos_enabled=False,
|
|
430
|
+
description="UART receive queue", title="UART receive queue"
|
|
431
|
+
))
|
|
432
|
+
|
|
433
|
+
defs.add_timer_variables()
|
|
434
|
+
|
|
435
|
+
return defs
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# statable/shared/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Shared project libraries.
|
|
4
|
+
|
|
5
|
+
These modules have no PySide6 dependency and can be used from the SDK
|
|
6
|
+
without installing the GUI extras (statable[gui]).
|
|
7
|
+
|
|
8
|
+
[v3.0 / G-1a]
|
|
9
|
+
Moved from statable_gui.libcntrl to allow SDK-only installs.
|
|
10
|
+
Backward-compat shims remain at statable_gui.libcntrl.* for a while.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .role_function_library import RoleFunctionLibrary, RoleFunction
|
|
14
|
+
from .condition_library import ConditionLibrary, ConditionTemplate
|
|
15
|
+
from .literal_library import LiteralLibrary, LiteralDefinition
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"RoleFunctionLibrary",
|
|
19
|
+
"RoleFunction",
|
|
20
|
+
"ConditionLibrary",
|
|
21
|
+
"ConditionTemplate",
|
|
22
|
+
"LiteralLibrary",
|
|
23
|
+
"LiteralDefinition",
|
|
24
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# statable/shared/condition_library.py
|
|
2
|
+
"""\nShared transition condition library\n"""
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class ConditionTemplate:
|
|
10
|
+
"""Shared transition condition template"""
|
|
11
|
+
name: str # Template name
|
|
12
|
+
condition: str # Condition expression (including literals)
|
|
13
|
+
description: str = ""
|
|
14
|
+
|
|
15
|
+
def to_dict(self) -> dict:
|
|
16
|
+
return {
|
|
17
|
+
'name': self.name,
|
|
18
|
+
'condition': self.condition,
|
|
19
|
+
'description': self.description,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def from_dict(cls, data: dict) -> 'ConditionTemplate':
|
|
24
|
+
return cls(
|
|
25
|
+
name=data.get('name', ''),
|
|
26
|
+
condition=data.get('condition', ''),
|
|
27
|
+
description=data.get('description', ''),
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConditionLibrary:
|
|
32
|
+
"""Transition condition library shared"""
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self.condition_templates: Dict[str, ConditionTemplate] = {}
|
|
36
|
+
|
|
37
|
+
def add(self, ct: ConditionTemplate):
|
|
38
|
+
if ct.name in self.condition_templates:
|
|
39
|
+
raise ValueError(f"Condition template '{ct.name}' already exists")
|
|
40
|
+
self.condition_templates[ct.name] = ct
|
|
41
|
+
|
|
42
|
+
def remove(self, name: str):
|
|
43
|
+
if name in self.condition_templates:
|
|
44
|
+
del self.condition_templates[name]
|
|
45
|
+
|
|
46
|
+
def get(self, name: str) -> ConditionTemplate:
|
|
47
|
+
return self.condition_templates.get(name)
|
|
48
|
+
|
|
49
|
+
def list_all(self) -> List[ConditionTemplate]:
|
|
50
|
+
return list(self.condition_templates.values())
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> dict:
|
|
53
|
+
return {
|
|
54
|
+
'condition_templates': [ct.to_dict() for ct in self.condition_templates.values()]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def from_dict(cls, data: dict) -> 'ConditionLibrary':
|
|
59
|
+
lib = cls()
|
|
60
|
+
for item in data.get('condition_templates', []):
|
|
61
|
+
ct = ConditionTemplate.from_dict(item)
|
|
62
|
+
lib.add(ct)
|
|
63
|
+
return lib
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# statable/shared/literal_library.py
|
|
2
|
+
"""\nShared literal library\n"""
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class LiteralDefinition:
|
|
10
|
+
"""Shared literal definition"""
|
|
11
|
+
name: str # Unique literal name
|
|
12
|
+
value: str # Value
|
|
13
|
+
literal_type: str = "int" # int / float / string / bool
|
|
14
|
+
description: str = ""
|
|
15
|
+
|
|
16
|
+
def to_dict(self) -> dict:
|
|
17
|
+
return {
|
|
18
|
+
'name': self.name,
|
|
19
|
+
'value': self.value,
|
|
20
|
+
'literal_type': self.literal_type,
|
|
21
|
+
'description': self.description,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_dict(cls, data: dict) -> 'LiteralDefinition':
|
|
26
|
+
return cls(
|
|
27
|
+
name=data.get('name', ''),
|
|
28
|
+
value=data.get('value', ''),
|
|
29
|
+
literal_type=data.get('literal_type', 'int'),
|
|
30
|
+
description=data.get('description', ''),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LiteralLibrary:
|
|
35
|
+
"""Literal library shared across project"""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.literals: Dict[str, LiteralDefinition] = {}
|
|
39
|
+
|
|
40
|
+
def add(self, lit: LiteralDefinition):
|
|
41
|
+
if lit.name in self.literals:
|
|
42
|
+
raise ValueError(f"Literal '{lit.name}' already exists")
|
|
43
|
+
self.literals[lit.name] = lit
|
|
44
|
+
|
|
45
|
+
def remove(self, name: str):
|
|
46
|
+
if name in self.literals:
|
|
47
|
+
del self.literals[name]
|
|
48
|
+
|
|
49
|
+
def get(self, name: str) -> LiteralDefinition:
|
|
50
|
+
return self.literals.get(name)
|
|
51
|
+
|
|
52
|
+
def list_all(self) -> List[LiteralDefinition]:
|
|
53
|
+
return list(self.literals.values())
|
|
54
|
+
|
|
55
|
+
def to_dict(self) -> dict:
|
|
56
|
+
return {
|
|
57
|
+
'literals': [lit.to_dict() for lit in self.literals.values()]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls, data: dict) -> 'LiteralLibrary':
|
|
62
|
+
lib = cls()
|
|
63
|
+
for item in data.get('literals', []):
|
|
64
|
+
lit = LiteralDefinition.from_dict(item)
|
|
65
|
+
lib.add(lit)
|
|
66
|
+
return lib
|