Skip to content

Commit 6c77e2e

Browse files
committed
Add control system (FastCS class) docs
1 parent 149e6a0 commit 6c77e2e

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# The FastCS class
2+
3+
`FastCS` is the entrypoint for a fastcs application. It connects a `Controller` to one
4+
or more `Transport`s, runs the controller's update loops as background tasks, and
5+
manages the full application lifecycle from startup to shutdown.
6+
7+
## Construction
8+
9+
```python
10+
from fastcs import FastCS
11+
from fastcs.controllers import Controller
12+
from fastcs.transports.epics import EpicsCATransport
13+
14+
15+
class MyController(Controller):
16+
pass
17+
18+
19+
control_system = FastCS(controller=MyController(), transports=[EpicsCATransport()])
20+
21+
control_system.run()
22+
```
23+
24+
## Startup and Runtime
25+
26+
Calling `control_system.run()` (or `await control_system.serve()`) executes the
27+
following steps in order:
28+
29+
1. **Initialise the controller:** `controller.initialise()` is awaited, allowing the
30+
controller to query the device and dynamically add attributes before the API is
31+
built. After that, `controller.post_initialise()` is called to perform any final
32+
setup, such as validating all type hints are satisfied.
33+
34+
2. **Build the API:** `controller.create_api_and_tasks()` returns the `ControllerAPI`
35+
that transports will use, plus two lists of coroutines: *initial tasks* (run once at
36+
startup) and *scan tasks* (run as continuous background loops).
37+
38+
3. **Connect transports:** each transport's `connect()` method is called with the
39+
`ControllerAPI`. This lets the transport inspect the controller's attributes and
40+
commands to prepare its protocol-specific representations before serving begins.
41+
42+
4. **Connect the controller:** `controller.connect()` is called to open the connection
43+
to the device and perform any other setup logic.
44+
45+
5. **Run initial tasks:** each initial-task coroutine is awaited in sequence. These are
46+
`@scan(period=ONCE)` methods and `AttributeIO` update callbacks with
47+
`update_period=ONCE`.
48+
49+
6. **Start scan tasks:** each scan task coroutine is wrapped in an `asyncio.Task` and
50+
run as a background task for the lifetime of the application.
51+
52+
7. **Gather transport coroutines:** `asyncio.gather` runs all transport `serve()`
53+
coroutines concurrently. Each transport begins accepting and responding to protocol
54+
requests.
55+
56+
8. **Scan pause and reconnect**: If any scan tasks raise exceptions, all scan tasks are
57+
paused until `reconnect`.
58+
59+
## The Interactive Shell
60+
61+
Alongside the transport coroutines, FastCS launches an embedded
62+
[IPython](https://ipython.org/) shell (unless `interactive=False` is passed). The shell
63+
namespace is pre-populated with:
64+
65+
| Name | Value |
66+
|------|-------|
67+
| `controller` | The root controller instance |
68+
| `transports` | The class names of active transports |
69+
| `run` | A helper that schedules a coroutine on the FastCS event loop from the IPython thread |
70+
| *transport-specific keys* | Any entries exposed via each transport's `context` property |
71+
72+
The shell runs in a separate thread so it does not block the asyncio event loop. When
73+
the user exits the shell, the application begins its shutdown sequence.
74+
75+
When `interactive=False` a simple coroutine that blocks forever keeps the application
76+
alive until the task is cancelled externally (e.g. SIGINT).
77+
78+
## Shutdown Sequence
79+
80+
When then run is stopped, FastCS performs an orderly teardown:
81+
82+
1. **Cancel scan tasks:** each background scan task is cancelled and removed, stopping
83+
all periodic polling.
84+
85+
2. **Disconnect the controller:** `controller.disconnect()` is awaited, allowing the
86+
controller to release device resources cleanly.

0 commit comments

Comments
 (0)