mbse 0.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 (84) hide show
  1. mbse/examples/elevator/README.md +39 -0
  2. mbse/examples/elevator/__init__.py +53 -0
  3. mbse/examples/elevator/__main__.py +6 -0
  4. mbse/examples/elevator/check_car_clearance.json +28 -0
  5. mbse/examples/elevator/choose_direction.json +56 -0
  6. mbse/examples/elevator/context.json +76 -0
  7. mbse/examples/elevator/elevator_hsm.json +294 -0
  8. mbse/examples/elevator/executables.py +119 -0
  9. mbse/examples/elevator/operate_doors.json +59 -0
  10. mbse/examples/elevator/plan_trip.json +27 -0
  11. mbse/examples/elevator/project.json +6 -0
  12. mbse/examples/elevator/travel_to_target.json +47 -0
  13. mbse/examples/elevator/wait_for_doorway_clearance.json +28 -0
  14. mbse/model/activity/activity_model.json +134 -0
  15. mbse/model/activity/activity_model.py +84 -0
  16. mbse/model/activity/doc/activity_model.md +43 -0
  17. mbse/model/context/context_model.json +37 -0
  18. mbse/model/context/context_model.py +55 -0
  19. mbse/model/context/doc/context_model.md +12 -0
  20. mbse/model/doc/model.md +34 -0
  21. mbse/model/hsm/doc/hsm_model.md +114 -0
  22. mbse/model/hsm/hsm_model.json +259 -0
  23. mbse/model/hsm/hsm_model.py +496 -0
  24. mbse/model/mbse_model.json +328 -0
  25. mbse/model/mbse_model.py +67 -0
  26. mbse/model/project/doc/project_model.md +22 -0
  27. mbse/model/project/project_model.json +32 -0
  28. mbse/model/project/project_model.py +26 -0
  29. mbse/model/project/project_registry.py +171 -0
  30. mbse/runtime/activity/activity_runtime.py +386 -0
  31. mbse/runtime/activity/doc/activity_runtime.md +39 -0
  32. mbse/runtime/doc/runtime.md +60 -0
  33. mbse/runtime/hsm/doc/hsm_runtime.md +60 -0
  34. mbse/runtime/hsm/hsm_runtime.py +984 -0
  35. mbse/runtime/runtime.py +481 -0
  36. mbse/runtime/runtime_signals.py +7 -0
  37. mbse-0.1.0.dist-info/METADATA +169 -0
  38. mbse-0.1.0.dist-info/RECORD +84 -0
  39. mbse-0.1.0.dist-info/WHEEL +5 -0
  40. mbse-0.1.0.dist-info/entry_points.txt +3 -0
  41. mbse-0.1.0.dist-info/licenses/LICENSE +21 -0
  42. mbse-0.1.0.dist-info/scm_file_list.json +127 -0
  43. mbse-0.1.0.dist-info/scm_version.json +8 -0
  44. mbse-0.1.0.dist-info/top_level.txt +2 -0
  45. mbse_web_viewer/render/activity/activity_render.dot.j2 +37 -0
  46. mbse_web_viewer/render/activity/activity_render.py +487 -0
  47. mbse_web_viewer/render/activity/activity_render_helpers.py +360 -0
  48. mbse_web_viewer/render/activity/activity_render_types.py +107 -0
  49. mbse_web_viewer/render/activity/doc/activity_render.md +12 -0
  50. mbse_web_viewer/render/doc/render.md +12 -0
  51. mbse_web_viewer/render/hsm/doc/hsm_render.md +44 -0
  52. mbse_web_viewer/render/hsm/hsm_render.dot.j2 +82 -0
  53. mbse_web_viewer/render/hsm/hsm_render.py +765 -0
  54. mbse_web_viewer/render/hsm/hsm_render_helpers.py +900 -0
  55. mbse_web_viewer/render/hsm/hsm_render_types.py +178 -0
  56. mbse_web_viewer/server/controller.py +653 -0
  57. mbse_web_viewer/server/debugging/activity_breakpoints.py +141 -0
  58. mbse_web_viewer/server/debugging/hsm_breakpoints.py +345 -0
  59. mbse_web_viewer/server/doc/server.md +59 -0
  60. mbse_web_viewer/server/highlighting/activity_highlighting.py +176 -0
  61. mbse_web_viewer/server/highlighting/hsm_highlighting.py +710 -0
  62. mbse_web_viewer/server/http_server.py +347 -0
  63. mbse_web_viewer/server/model_catalog.py +64 -0
  64. mbse_web_viewer/server/session.py +69 -0
  65. mbse_web_viewer/server/viewer_app.py +74 -0
  66. mbse_web_viewer/static/assets/debugger-pause.svg +4 -0
  67. mbse_web_viewer/static/assets/debugger-play.svg +3 -0
  68. mbse_web_viewer/static/assets/debugger-reset.svg +4 -0
  69. mbse_web_viewer/static/assets/debugger-step-into.svg +4 -0
  70. mbse_web_viewer/static/assets/debugger-step-out.svg +4 -0
  71. mbse_web_viewer/static/assets/debugger-step.svg +4 -0
  72. mbse_web_viewer/static/doc/static.md +22 -0
  73. mbse_web_viewer/static/index.html +153 -0
  74. mbse_web_viewer/static/viewer.css +664 -0
  75. mbse_web_viewer/static/viewer.js +698 -0
  76. mbse_web_viewer/static/viewer_api.js +16 -0
  77. mbse_web_viewer/static/viewer_breakpoints.js +389 -0
  78. mbse_web_viewer/static/viewer_debugger.js +64 -0
  79. mbse_web_viewer/static/viewer_declared_values.js +172 -0
  80. mbse_web_viewer/static/viewer_highlights.js +140 -0
  81. mbse_web_viewer/static/viewer_models.js +84 -0
  82. mbse_web_viewer/static/viewer_state.js +61 -0
  83. mbse_web_viewer/static/viewer_utils.js +16 -0
  84. mbse_web_viewer/static/viewer_viewport.js +285 -0
@@ -0,0 +1,39 @@
1
+ # Elevator Example
2
+
3
+ This packaged example models one elevator serving a floor request. It is a
4
+ small, runnable viewer demonstration rather than a production elevator design.
5
+
6
+ Run it after installing `mbse`:
7
+
8
+ ```bash
9
+ mbse-example-view
10
+ ```
11
+
12
+ Send `Request elevator` with a pickup floor, then `Select destination` once the
13
+ viewer reaches `Awaiting destination`. Follow execution with step into, step
14
+ over, and step out.
15
+
16
+ ## Use Case
17
+
18
+ The HSM has an `operational` composite state with one nested `servicing` cycle:
19
+ `planning`, `travelling`, and `doors`. The shared `service_phase` Context value
20
+ uses that cycle first for pickup and then for delivery.
21
+
22
+ The service cycle invokes nested Activities to plan the trip, choose a
23
+ direction, verify movement clearance, travel, and operate the doors.
24
+ Clearance Activities set the shared `result` variable, which their parent
25
+ decisions use. Requests for the current floor open the pickup doors directly;
26
+ selecting the current floor does not start a delivery trip.
27
+
28
+ `emergency_stop` is handled by `operational`, not by its leaf states. Sending
29
+ it while any operational child is active demonstrates HSM event bubbling to the
30
+ shared parent transition. A blocked door sequence enters `Doorway blocked`.
31
+ `reset` exits either that state or `Emergency stop`.
32
+
33
+ This demonstrates HSM events, shared Context variables, model executable
34
+ references, nested runtime frames, and viewer model navigation. The authored
35
+ models and handler implementation remain the source of truth.
36
+
37
+ Related documentation: [Model Layer](../../model/doc/model.md),
38
+ [Runtime Layer](../../runtime/doc/runtime.md), and
39
+ [Viewer Server](../../../../mbse_web_viewer/server/doc/server.md).
@@ -0,0 +1,53 @@
1
+ """Packaged elevator example for the MBSE web viewer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from mbse_web_viewer.server.http_server import RunningViewerServer
9
+ from mbse_web_viewer.server.viewer_app import startProjectViewerServerFromProjectPath
10
+
11
+
12
+ def getProjectPath() -> Path:
13
+ """Return the packaged elevator project file."""
14
+
15
+ return Path(__file__).with_name("project.json")
16
+
17
+
18
+ def startViewer(
19
+ *,
20
+ host: str = "127.0.0.1",
21
+ port: int = 0,
22
+ open_browser: bool = False,
23
+ ) -> RunningViewerServer:
24
+ """Start the local viewer for the packaged elevator example."""
25
+
26
+ return startProjectViewerServerFromProjectPath(
27
+ getProjectPath(),
28
+ host=host,
29
+ port=port,
30
+ open_browser=open_browser,
31
+ )
32
+
33
+
34
+ def main(argv: list[str] | None = None) -> int:
35
+ """Launch the packaged elevator example from the command line."""
36
+
37
+ parser = argparse.ArgumentParser(description="Launch the MBSE elevator example.")
38
+ parser.add_argument("--host", default="127.0.0.1")
39
+ parser.add_argument("--port", default=0, type=int)
40
+ parser.add_argument("--open-browser", action="store_true")
41
+ args = parser.parse_args(argv)
42
+
43
+ server = startViewer(
44
+ host=args.host,
45
+ port=args.port,
46
+ open_browser=args.open_browser,
47
+ )
48
+ print(server.base_url)
49
+ try:
50
+ server.waitUntilStopped()
51
+ except KeyboardInterrupt:
52
+ server.close()
53
+ return 0
@@ -0,0 +1,6 @@
1
+ """Run the packaged elevator example as a module."""
2
+
3
+ from mbse.examples.elevator import main
4
+
5
+
6
+ raise SystemExit(main())
@@ -0,0 +1,28 @@
1
+ {
2
+ "schema_version": "mbse-activity-model-v0",
3
+ "document_id": "check_car_clearance",
4
+ "initial": {
5
+ "target_id": "evaluate_emergency"
6
+ },
7
+ "actions": [
8
+ {
9
+ "id": "evaluate_emergency",
10
+ "label": "Evaluate emergency state",
11
+ "executable": {
12
+ "kind": "action_language",
13
+ "module": "mbse.examples.elevator.executables",
14
+ "name": "isCarClear"
15
+ },
16
+ "transition": {
17
+ "target_id": "clearance_checked"
18
+ }
19
+ }
20
+ ],
21
+ "decisions": [],
22
+ "finals": [
23
+ {
24
+ "id": "clearance_checked",
25
+ "label": "Clearance checked"
26
+ }
27
+ ]
28
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "schema_version": "mbse-activity-model-v0",
3
+ "document_id": "choose_direction",
4
+ "initial": {
5
+ "target_id": "target_is_above"
6
+ },
7
+ "actions": [
8
+ {
9
+ "id": "set_direction_up",
10
+ "label": "Set direction up",
11
+ "executable": {
12
+ "kind": "action_language",
13
+ "module": "mbse.examples.elevator.executables",
14
+ "name": "setDirectionUp"
15
+ },
16
+ "transition": {
17
+ "target_id": "direction_selected"
18
+ }
19
+ },
20
+ {
21
+ "id": "set_direction_down",
22
+ "label": "Set direction down",
23
+ "executable": {
24
+ "kind": "action_language",
25
+ "module": "mbse.examples.elevator.executables",
26
+ "name": "setDirectionDown"
27
+ },
28
+ "transition": {
29
+ "target_id": "direction_selected"
30
+ }
31
+ }
32
+ ],
33
+ "decisions": [
34
+ {
35
+ "id": "target_is_above",
36
+ "label": "Target is above current floor",
37
+ "condition": {
38
+ "kind": "action_language",
39
+ "module": "mbse.examples.elevator.executables",
40
+ "name": "targetIsAboveCurrent"
41
+ },
42
+ "true_transition": {
43
+ "target_id": "set_direction_up"
44
+ },
45
+ "false_transition": {
46
+ "target_id": "set_direction_down"
47
+ }
48
+ }
49
+ ],
50
+ "finals": [
51
+ {
52
+ "id": "direction_selected",
53
+ "label": "Direction selected"
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "schema_version": "mbse-context-model-v0",
3
+ "document_id": "elevator_context",
4
+ "enums": [
5
+ {
6
+ "id": "direction",
7
+ "values": ["up", "down"]
8
+ },
9
+ {
10
+ "id": "service_phase",
11
+ "values": ["pickup", "delivery"]
12
+ }
13
+ ],
14
+ "variables": [
15
+ {
16
+ "name": "current_floor",
17
+ "type": "unsigned",
18
+ "min": 0,
19
+ "max": 10,
20
+ "default_value": 0
21
+ },
22
+ {
23
+ "name": "target_floor",
24
+ "type": "unsigned",
25
+ "min": 0,
26
+ "max": 10,
27
+ "default_value": 0
28
+ },
29
+ {
30
+ "name": "pickup_floor",
31
+ "type": "unsigned",
32
+ "min": 0,
33
+ "max": 10,
34
+ "default_value": 0
35
+ },
36
+ {
37
+ "name": "destination_floor",
38
+ "type": "unsigned",
39
+ "min": 0,
40
+ "max": 10,
41
+ "default_value": 0
42
+ },
43
+ {
44
+ "name": "direction",
45
+ "type": "enum",
46
+ "enum_id": "direction",
47
+ "default_value": "up"
48
+ },
49
+ {
50
+ "name": "service_phase",
51
+ "type": "enum",
52
+ "enum_id": "service_phase",
53
+ "default_value": "pickup"
54
+ },
55
+ {
56
+ "name": "doorway_clear",
57
+ "type": "bool",
58
+ "default_value": true
59
+ },
60
+ {
61
+ "name": "doors_open",
62
+ "type": "bool",
63
+ "default_value": false
64
+ },
65
+ {
66
+ "name": "emergency_active",
67
+ "type": "bool",
68
+ "default_value": false
69
+ },
70
+ {
71
+ "name": "result",
72
+ "type": "bool",
73
+ "default_value": false
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,294 @@
1
+ {
2
+ "schema_version": "mbse-hsm-model-v0",
3
+ "document_id": "elevator_hsm",
4
+ "events": [
5
+ {
6
+ "id": "request_elevator",
7
+ "label": "Request elevator",
8
+ "parameters": [
9
+ {
10
+ "name": "floor",
11
+ "type": "unsigned",
12
+ "min": 0,
13
+ "max": 10
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "id": "select_destination",
19
+ "label": "Select destination",
20
+ "parameters": [
21
+ {
22
+ "name": "floor",
23
+ "type": "unsigned",
24
+ "min": 0,
25
+ "max": 10
26
+ }
27
+ ]
28
+ },
29
+ {
30
+ "id": "trip_ready",
31
+ "label": "Trip ready"
32
+ },
33
+ {
34
+ "id": "arrived",
35
+ "label": "Arrived"
36
+ },
37
+ {
38
+ "id": "doors_ready",
39
+ "label": "Doors ready"
40
+ },
41
+ {
42
+ "id": "doorway_blocked",
43
+ "label": "Doorway blocked"
44
+ },
45
+ {
46
+ "id": "emergency_stop",
47
+ "label": "Emergency stop"
48
+ },
49
+ {
50
+ "id": "reset",
51
+ "label": "Reset"
52
+ }
53
+ ],
54
+ "initial_transition": {
55
+ "target_id": "operational"
56
+ },
57
+ "states": [
58
+ {
59
+ "id": "operational",
60
+ "label": "Operational",
61
+ "initial_transition": {
62
+ "target_id": "idle"
63
+ },
64
+ "external_transitions": [
65
+ {
66
+ "event_id": "emergency_stop",
67
+ "target_id": "emergency",
68
+ "activities": [
69
+ {
70
+ "kind": "action_language",
71
+ "module": "mbse.examples.elevator.executables",
72
+ "name": "activateEmergency"
73
+ }
74
+ ]
75
+ }
76
+ ],
77
+ "states": [
78
+ {
79
+ "id": "idle",
80
+ "label": "Idle",
81
+ "external_transitions": [
82
+ {
83
+ "event_id": "request_elevator",
84
+ "guard_condition": {
85
+ "guard_activity": {
86
+ "kind": "action_language",
87
+ "module": "mbse.examples.elevator.executables",
88
+ "name": "requestedFloorIsCurrent"
89
+ },
90
+ "true_branch": {
91
+ "target_id": "doors",
92
+ "activities": [
93
+ {
94
+ "kind": "action_language",
95
+ "module": "mbse.examples.elevator.executables",
96
+ "name": "capturePickupFloor"
97
+ },
98
+ {
99
+ "kind": "action_language",
100
+ "module": "mbse.examples.elevator.executables",
101
+ "name": "setPickupPhase"
102
+ }
103
+ ]
104
+ },
105
+ "false_branch": {
106
+ "target_id": "servicing",
107
+ "activities": [
108
+ {
109
+ "kind": "action_language",
110
+ "module": "mbse.examples.elevator.executables",
111
+ "name": "capturePickupFloor"
112
+ },
113
+ {
114
+ "kind": "action_language",
115
+ "module": "mbse.examples.elevator.executables",
116
+ "name": "setPickupPhase"
117
+ }
118
+ ]
119
+ }
120
+ }
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "id": "servicing",
126
+ "label": "Service trip",
127
+ "initial_transition": {
128
+ "target_id": "planning"
129
+ },
130
+ "states": [
131
+ {
132
+ "id": "planning",
133
+ "label": "Planning trip",
134
+ "hooks": {
135
+ "on_entry": [
136
+ {
137
+ "kind": "model",
138
+ "model_id": "plan_trip"
139
+ },
140
+ {
141
+ "kind": "action_language",
142
+ "module": "mbse.examples.elevator.executables",
143
+ "name": "queueTripReady"
144
+ }
145
+ ]
146
+ },
147
+ "external_transitions": [
148
+ {
149
+ "event_id": "trip_ready",
150
+ "target_id": "travelling"
151
+ }
152
+ ]
153
+ },
154
+ {
155
+ "id": "travelling",
156
+ "label": "Travelling",
157
+ "hooks": {
158
+ "on_entry": [
159
+ {
160
+ "kind": "model",
161
+ "model_id": "travel_to_target"
162
+ },
163
+ {
164
+ "kind": "action_language",
165
+ "module": "mbse.examples.elevator.executables",
166
+ "name": "queueArrival"
167
+ }
168
+ ]
169
+ },
170
+ "external_transitions": [
171
+ {
172
+ "event_id": "arrived",
173
+ "target_id": "doors"
174
+ }
175
+ ]
176
+ },
177
+ {
178
+ "id": "doors",
179
+ "label": "Door cycle",
180
+ "hooks": {
181
+ "on_entry": [
182
+ {
183
+ "kind": "model",
184
+ "model_id": "operate_doors"
185
+ },
186
+ {
187
+ "kind": "action_language",
188
+ "module": "mbse.examples.elevator.executables",
189
+ "name": "queueDoorResult"
190
+ }
191
+ ]
192
+ },
193
+ "external_transitions": [
194
+ {
195
+ "event_id": "doors_ready",
196
+ "guard_condition": {
197
+ "guard_activity": {
198
+ "kind": "action_language",
199
+ "module": "mbse.examples.elevator.executables",
200
+ "name": "isPickupPhase"
201
+ },
202
+ "true_branch": {
203
+ "target_id": "awaiting_destination"
204
+ },
205
+ "false_branch": {
206
+ "target_id": "idle"
207
+ }
208
+ }
209
+ },
210
+ {
211
+ "event_id": "doorway_blocked",
212
+ "target_id": "blocked"
213
+ }
214
+ ]
215
+ }
216
+ ]
217
+ },
218
+ {
219
+ "id": "awaiting_destination",
220
+ "label": "Awaiting destination",
221
+ "external_transitions": [
222
+ {
223
+ "event_id": "select_destination",
224
+ "guard_condition": {
225
+ "guard_activity": {
226
+ "kind": "action_language",
227
+ "module": "mbse.examples.elevator.executables",
228
+ "name": "requestedFloorIsCurrent"
229
+ },
230
+ "true_branch": {
231
+ "target_id": "awaiting_destination",
232
+ "activities": [
233
+ {
234
+ "kind": "action_language",
235
+ "module": "mbse.examples.elevator.executables",
236
+ "name": "captureDestinationFloor"
237
+ },
238
+ {
239
+ "kind": "action_language",
240
+ "module": "mbse.examples.elevator.executables",
241
+ "name": "setDeliveryPhase"
242
+ }
243
+ ]
244
+ },
245
+ "false_branch": {
246
+ "target_id": "servicing",
247
+ "activities": [
248
+ {
249
+ "kind": "action_language",
250
+ "module": "mbse.examples.elevator.executables",
251
+ "name": "captureDestinationFloor"
252
+ },
253
+ {
254
+ "kind": "action_language",
255
+ "module": "mbse.examples.elevator.executables",
256
+ "name": "setDeliveryPhase"
257
+ }
258
+ ]
259
+ }
260
+ }
261
+ }
262
+ ]
263
+ },
264
+ {
265
+ "id": "blocked",
266
+ "label": "Doorway blocked",
267
+ "external_transitions": [
268
+ {
269
+ "event_id": "reset",
270
+ "target_id": "idle"
271
+ }
272
+ ]
273
+ }
274
+ ]
275
+ },
276
+ {
277
+ "id": "emergency",
278
+ "label": "Emergency stop",
279
+ "external_transitions": [
280
+ {
281
+ "event_id": "reset",
282
+ "target_id": "idle",
283
+ "activities": [
284
+ {
285
+ "kind": "action_language",
286
+ "module": "mbse.examples.elevator.executables",
287
+ "name": "clearEmergency"
288
+ }
289
+ ]
290
+ }
291
+ ]
292
+ }
293
+ ]
294
+ }
@@ -0,0 +1,119 @@
1
+ """Action-language handlers for the packaged elevator example."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ def capturePickupFloor(ctx) -> None:
7
+ """Store the floor where the passenger requested service."""
8
+
9
+ ctx.pickup_floor = ctx.event_parameters["floor"]
10
+ ctx.target_floor = ctx.pickup_floor
11
+
12
+
13
+ def captureDestinationFloor(ctx) -> None:
14
+ """Store the floor selected from inside the elevator."""
15
+
16
+ ctx.destination_floor = ctx.event_parameters["floor"]
17
+ ctx.target_floor = ctx.destination_floor
18
+
19
+
20
+ def requestedFloorIsCurrent(ctx) -> bool:
21
+ """Return whether an event requests the elevator's current floor."""
22
+
23
+ return ctx.event_parameters["floor"] == ctx.current_floor
24
+
25
+
26
+ def setPickupPhase(ctx) -> None:
27
+ """Mark the shared service cycle as a passenger pickup."""
28
+
29
+ ctx.service_phase = "pickup"
30
+
31
+
32
+ def setDeliveryPhase(ctx) -> None:
33
+ """Mark the shared service cycle as a passenger delivery."""
34
+
35
+ ctx.service_phase = "delivery"
36
+
37
+
38
+ def isPickupPhase(ctx) -> bool:
39
+ """Return whether the completed door cycle belongs to a pickup."""
40
+
41
+ return ctx.service_phase == "pickup"
42
+
43
+
44
+ def queueTripReady(ctx) -> None:
45
+ """Continue from trip planning to travel."""
46
+
47
+ ctx.send_event("trip_ready")
48
+
49
+
50
+ def queueArrival(ctx) -> None:
51
+ """Continue from travel to the door cycle."""
52
+
53
+ ctx.send_event("arrived")
54
+
55
+
56
+ def queueDoorResult(ctx) -> None:
57
+ """Advance or block the HSM according to the nested door Activity result."""
58
+
59
+ ctx.send_event("doors_ready" if ctx.result else "doorway_blocked")
60
+
61
+
62
+ def targetIsAboveCurrent(ctx) -> bool:
63
+ """Return whether the elevator must travel upward."""
64
+
65
+ return ctx.target_floor > ctx.current_floor
66
+
67
+
68
+ def setDirectionUp(ctx) -> None:
69
+ """Record upward travel."""
70
+
71
+ ctx.direction = "up"
72
+
73
+
74
+ def setDirectionDown(ctx) -> None:
75
+ """Record downward travel."""
76
+
77
+ ctx.direction = "down"
78
+
79
+
80
+ def isCarClear(ctx) -> None:
81
+ """Return whether movement is permitted through the shared result variable."""
82
+
83
+ ctx.result = not ctx.emergency_active
84
+
85
+
86
+ def moveToTargetFloor(ctx) -> None:
87
+ """Complete the simulated movement."""
88
+
89
+ ctx.current_floor = ctx.target_floor
90
+
91
+
92
+ def isDoorwayClear(ctx) -> None:
93
+ """Return whether the door sequence may run."""
94
+
95
+ ctx.result = ctx.doorway_clear
96
+
97
+
98
+ def openDoors(ctx) -> None:
99
+ """Record an open-door phase."""
100
+
101
+ ctx.doors_open = True
102
+
103
+
104
+ def closeDoors(ctx) -> None:
105
+ """Record a completed door cycle."""
106
+
107
+ ctx.doors_open = False
108
+
109
+
110
+ def activateEmergency(ctx) -> None:
111
+ """Stop future movement until reset."""
112
+
113
+ ctx.emergency_active = True
114
+
115
+
116
+ def clearEmergency(ctx) -> None:
117
+ """Return the controller to normal operation."""
118
+
119
+ ctx.emergency_active = False