DiamondLightSource / DiamondLightSource/fastcs

Example 4 — SCPI device: annotated attributes + per-attribute filler data

Open
#405 1 comment 0 reactions 0 assignees View on GitHub
api-convergence
Dominant language
Python
Stars
6
Forks
8
Avg merge
2d 12h
Merged PRs (30d)
3

Description

Part of the tutorial examples (#388). **Tutorial 3 — declarative:** a NON-introspectable device whose attributes are declared as annotated hints, with each attribute's getter/setter provisioned from per-attribute filler metadata (extras mechanism, #388 decision 3) via `ControllerFiller`.

Runs on the **temperature-controller backend** — its text protocol is already SCPI-shaped, so no new sim is needed and, crucially, **no introspection is invented**. A SCPI device does not describe itself; that is *why* you hand-annotate, and keeping it non-introspectable preserves the contrast with the introspectable Eiger tutorial (#391).

**This tutorial also carries composition + methods** (the old "io= pattern" tutorial is gone): it walks the **full multi-ramp temperature controller** — ramp **sub-controllers** / `ControllerVector`, the voltage `@scan`, the cancel-all `@command` — declared via `SCPIParam`, drawing on the composition example (`controllers.py`, #390).

## Scope
- Add `src/fastcs/demo/scpi.py`: an example `SCPIController` + `SCPIParam` extras vocabulary. **This lives in the demo (protocol) layer, NOT core FastCS** (decision 3: core ships no extras vocabulary for 1.0) — it demonstrates how a third party builds a protocol layer on the filler's `(child, extras)` yield.
- Add `src/fastcs/demo/temperature_scpi.py`: the full temperature controller as annotated hints — `Annotated[AttrRW[float], SCPIParam("P", precision=3, units="W", description=…)]` — with the `SCPIController`'s `ControllerFiller` building each attribute's **getter/setter** (and applying `.meta`) from that static metadata; plus sub-controllers/vector + `@scan` + `@command`.
- Unit tests against the sim, including the negative case below.

## SCPIParam — one spec object per attribute (ADR 0014)
The canonical demo of the common declarative spelling.
- `SCPIParam(param: str, **kwargs: Unpack[Meta])` (`Meta` = the superset TypedDict) is the **single place** an attribute's whole spec is written: the command token **plus** all generic metadata (`description`, `precision`, `units`, limits…). It stores a `.meta`.
- **Exclusive** spec source for its attribute — the filler does not also merge a separate `FloatMeta`/`Meta` extra on the same hint.
- The filler builds the getter/setter and passes `.meta` into the constructed attribute, **runtime-validating** it against the datatype.
- Named `SCPIParam` (a binding extra you instantiate, sibling of `PvSuffix`), **not** `SCPIMeta` — the `*Meta` suffix is reserved for the `Unpack`-able TypedDicts.
- Show a valid case, and (in a test) that supplying `precision` for a `str` attribute raises with a message naming the attribute + field.

## Acceptance
`uv run --locked tox -e pre-commit,type-checking,tests` green.

ADR: docs/explanations/decisions/0013-declarative-procedural-split-and-controller-filler.md, 0014
Parent: #388
Blocked by: #394

Contributor guide

Open the contributing guide

Research direction

Start with docs/explanations/decisions/0013-declarative-procedural-split-and-controller-filler.md and 0014, then inspect the temperature-controller simulator and the blocked dependency #394. Implement the examples in src/fastcs/demo/scpi.py and src/fastcs/demo/temperature_scpi.py with unit tests covering valid metadata and the invalid string precision case. Done means uv run --locked tox -e pre-commit,type-checking,tests is green.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.