mirror of
https://github.com/bec-project/ophyd_devices.git
synced 2026-10-08 06:14:54 +02:00
docs(AGENTS): update repository guidelines for clarity and structure
This commit is contained in:
@@ -1,207 +1,282 @@
|
||||
# Repository Guidelines — `ophyd_devices`
|
||||
|
||||
`ophyd_devices` is the **hardware abstraction layer** for
|
||||
[BEC (Beamline Experiment Control)](https://github.com/bec-project/bec). It extends
|
||||
[ophyd](https://github.com/bluesky/ophyd) with device support for hardware that the standard EPICS
|
||||
implementation does not cover — motion controllers, detectors, shutters, undulators, monochromators —
|
||||
plus a full simulation framework so BEC can run end-to-end with no hardware attached.
|
||||
`ophyd_devices` provides reusable ophyd hardware support and simulation devices for
|
||||
[BEC](https://github.com/bec-project/bec). Prefer focused changes, follow existing local patterns,
|
||||
and verify the smallest relevant test scope.
|
||||
|
||||
This file is a quick-reference for AI coding agents (and new contributors). User-facing documentation
|
||||
lives at <https://bec.readthedocs.io>; general ophyd concepts are documented at
|
||||
<https://blueskyproject.io/ophyd/>.
|
||||
This file is an agent-oriented operating manual. User-facing documentation lives at
|
||||
<https://bec.readthedocs.io> and is authored in the separate `bec_docs` repository.
|
||||
Treat `pyproject.toml` as the source of truth for dependencies, scripts, and tool configuration.
|
||||
|
||||
## Project Structure & Module Organization
|
||||
## Core Rules
|
||||
|
||||
`ophyd_devices/` is the importable package:
|
||||
- Use `PSIDeviceBase` when a device needs BEC lifecycle or business logic. A device that merely
|
||||
exposes signals can use plain `ophyd.Device`, regardless of the communication backend.
|
||||
- Separate reusable device control from beamline-specific business logic in `on_*` hooks.
|
||||
- Prefer repository statuses, signals, and helpers when a BEC-aware counterpart exists.
|
||||
- Return promptly from device-server calls. Represent unfinished work with a status.
|
||||
- Implement safe interruption through `on_stop()` and register cancellable statuses.
|
||||
- Use `bec_signals.py` for BEC live data, progress, and file events.
|
||||
- Check the relevant device protocols when changing interfaces.
|
||||
- Add an example configuration for each new reusable device family.
|
||||
- Do not edit `ophyd_devices/devices/device_list.md`; CI generates it.
|
||||
- Follow existing local patterns before introducing a new abstraction.
|
||||
- Keep diffs focused and preserve unrelated local changes.
|
||||
- Add regression tests for bug fixes.
|
||||
- Do not commit, push, open a PR, or post a review unless explicitly requested.
|
||||
|
||||
| Path | What goes there |
|
||||
| --- | --- |
|
||||
| `ophyd_devices/interfaces/base_classes/` | The classes you should inherit from: `PSIDeviceBase`, `PSIPositionerBase`, `PSIPseudoDeviceBase`, `PSIPseudoMotorBase`. Start here before writing a device. |
|
||||
| `ophyd_devices/interfaces/protocols/` | `typing.Protocol` definitions (`BECDeviceProtocol`, `BECPositionerProtocol`, `BECFlyerProtocol`, …) describing what BEC expects from a device. Useful as a checklist and in `isinstance` tests. |
|
||||
| `ophyd_devices/interfaces/device_config_templates/` | Templates for generating device configuration entries. |
|
||||
| `ophyd_devices/devices/` | Concrete device implementations (`psi_motor.py`, `undulator.py`, `optics_shutter.py`, `dxp.py`, `areadetector/`, `panda_box/`, …), plus the generated `device_list.md`. |
|
||||
| `ophyd_devices/sim/` | The simulation framework: `SimPositioner`, `SimCamera`, `SimMonitor`, `SimWaveform`, `SimFlyer`, and the `sim_data.py` data generators behind them. |
|
||||
| `ophyd_devices/utils/` | Shared helpers: `bec_signals.py`, `bec_scaninfo_mixin.py`, `controller.py`, `socket.py`, `psi_device_base_utils.py` (`FileHandler`, `TaskHandler`), `static_device_test.py`. |
|
||||
| `ophyd_devices/configs/` | Example device configuration YAML files, including the simulation config used for local BEC runs. |
|
||||
| `ophyd_devices/npoint/`, `rt_lamni/`, `sls_devices/`, `smaract/` | Vendor- and facility-specific integrations. |
|
||||
| `tests/` | The test suite (flat; one `test_<area>.py` per area). |
|
||||
## First Read
|
||||
|
||||
`ophyd_devices/devices/device_list.md` is **generated by CI** on pushes to `main` — do not edit it by hand.
|
||||
Read the affected implementation and its tests. For unfamiliar areas, start here:
|
||||
|
||||
## Local Environment Overlay
|
||||
- `ophyd_devices/interfaces/base_classes/psi_device_base.py` — device lifecycle and BEC integration
|
||||
- `ophyd_devices/interfaces/base_classes/psi_positioner_base.py` — motion
|
||||
- `ophyd_devices/interfaces/base_classes/psi_pseudo_device_base.py` — pseudo devices
|
||||
- `ophyd_devices/interfaces/base_classes/psi_pseudo_motor_base.py` — pseudo motors
|
||||
- `ophyd_devices/interfaces/protocols/bec_protocols.py` — expected device contracts
|
||||
- `ophyd_devices/utils/psi_device_base_utils.py` — statuses, tasks, and file helpers
|
||||
- `ophyd_devices/utils/bec_signals.py` — live data publishing
|
||||
- `tests/conftest.py` and `ophyd_devices/tests/utils.py` — reusable test helpers
|
||||
- `README.md` — project overview
|
||||
|
||||
If a file named **`AGENTS_PERSONAL.md`** exists next to this one, read it and treat it as an extension
|
||||
of this file. It carries machine-specific setup — interpreter and environment manager, local paths,
|
||||
private workflow conventions — and **its instructions take precedence over the generic
|
||||
"Development Environment" section below**. Everything else in this file still applies.
|
||||
## Repo Layout
|
||||
|
||||
That file is intentionally untracked and personal to one developer's machine. Do not commit it, do not
|
||||
reference it from committed files, and do not assume it exists — if it is absent, follow this file as
|
||||
written.
|
||||
- `ophyd_devices/interfaces/` — base classes, protocols, and device configuration templates
|
||||
- `ophyd_devices/devices/` — concrete devices and the generated device list
|
||||
- `ophyd_devices/sim/` — simulation devices and data generators
|
||||
- `ophyd_devices/utils/` — shared signals, statuses, controllers, and other helpers
|
||||
- `ophyd_devices/configs/` — example device configurations
|
||||
- `tests/` — unit tests
|
||||
|
||||
## Development Environment
|
||||
Related but separate repositories:
|
||||
|
||||
Requires **Python 3.11+** (CI tests 3.11, 3.12, 3.13).
|
||||
- `bec` — core messaging, scans, services, and the client
|
||||
- `bec_widgets` — GUI widgets
|
||||
- `bec_docs` — published documentation
|
||||
- beamline plugin repositories — beamline-specific devices, scans, and widgets
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # macOS/Linux only; see "Platform Notes"
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -e '.[dev]'
|
||||
```
|
||||
## Local Overlay
|
||||
|
||||
The `dev` extra pulls in `bec-server`, which is what the device-server-facing tests exercise. Verify the
|
||||
environment resolves to this checkout:
|
||||
If `AGENTS_PERSONAL.md` exists beside this file, read it as an extension of these instructions.
|
||||
Its machine-specific environment and workflow guidance takes precedence over the corresponding
|
||||
sections here. Keep it local and untracked; do not copy its contents into committed files.
|
||||
|
||||
```bash
|
||||
python -c "import ophyd_devices; print(ophyd_devices.__file__)"
|
||||
```
|
||||
## Common Task Routing
|
||||
|
||||
No EPICS IOC or hardware is needed for development: unit tests mock connections, and the `sim/` devices
|
||||
provide a full working beamline in software.
|
||||
If you change:
|
||||
|
||||
## Writing a Device
|
||||
- `ophyd_devices/interfaces/base_classes/*`: inspect relevant protocols and affected concrete and
|
||||
simulation devices; test staging, subscriptions, movement, and stop behavior.
|
||||
- `ophyd_devices/utils/psi_device_base_utils.py`: check timeout handling and composition across
|
||||
status subclasses.
|
||||
- `ophyd_devices/utils/bec_signals.py`: check device-server consumers and report compatibility
|
||||
risks for `bec` and `bec_widgets`.
|
||||
- `ophyd_devices/sim/*`: check the affected device and tests consuming its scan data.
|
||||
- `ophyd_devices/devices/*` or vendor integrations: add targeted device tests; include an example
|
||||
configuration and validation notes for a new reusable device family.
|
||||
- `ophyd_devices/configs/*`: run `ophyd_test --config <changed-config>` and inspect its report.
|
||||
- Documentation or templates: verify referenced paths, commands, examples, and metadata.
|
||||
A broad unit test run is unnecessary when executable behavior is unchanged.
|
||||
|
||||
**Inherit from a `PSI*` base class, not from `ophyd.Device` directly.** `PSIDeviceBase` wires up the
|
||||
subscription types BEC's device manager expects (`readback`, `value`, `done_moving`, `motor_is_moving`,
|
||||
`progress`, `file_event`, `device_monitor_1d`, `device_monitor_2d`), gives you `scan_info` and
|
||||
`device_manager`, and provides the `FileHandler` / `TaskHandler` utilities. A bare `ophyd.Device` will
|
||||
appear to work locally and then misbehave inside a running BEC deployment.
|
||||
Reusable hardware support belongs here. Devices specific to one beamline belong in its plugin
|
||||
repository. Route core messaging, scans, and service changes to `bec`, GUI behavior to
|
||||
`bec_widgets`, and published documentation to `bec_docs`.
|
||||
|
||||
```python
|
||||
from ophyd import Component as Cpt, EpicsSignal, EpicsSignalRO
|
||||
## Writing A Device
|
||||
|
||||
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
|
||||
### Control and business logic
|
||||
|
||||
The device's base control class defines how to communicate with the hardware: signal
|
||||
definitions, commands, protocol handling, and device state. Keep this layer reusable across
|
||||
beamlines and implement it in `ophyd_devices`. Plain ophyd control classes may be composed into
|
||||
or combined with a class that uses `PSIDeviceBase` when business logic is needed.
|
||||
|
||||
class MyDetector(PSIDeviceBase):
|
||||
"""One-line description; this text reaches the generated device list."""
|
||||
A device that only exposes a collection of signals needs no business-logic layer; plain
|
||||
`ophyd.Device` is enough. For example, `SLSOperatorMessages` in
|
||||
`ophyd_devices/devices/sls_devices.py` groups operator messages and their dates using dynamic
|
||||
components without scan-specific behavior. This applies equally to EPICS and other communication
|
||||
backends; the need for business logic determines the base class, not the transport.
|
||||
|
||||
acquire = Cpt(EpicsSignal, "ACQ", kind="omitted")
|
||||
readback = Cpt(EpicsSignalRO, "VAL", kind="hinted")
|
||||
The `on_*` hooks typically describe business logic: how a beamline uses that control interface
|
||||
during a scan. For example, the control class exposes acquisition and trigger-mode commands;
|
||||
a beamline's `on_stage()` chooses the trigger mode and acquisition settings for its experiment,
|
||||
and `on_trigger()` starts acquisition through the control interface.
|
||||
|
||||
def on_stage(self) -> None:
|
||||
... # prepare for a scan
|
||||
Implement bespoke hook behavior in a subclass in the beamline plugin repository. Keep shared
|
||||
lifecycle behavior generic, and make hooks call reusable control methods rather than duplicating
|
||||
PV definitions or communication code. This lets beamlines share the same hardware support while
|
||||
choosing their own acquisition and scan behavior.
|
||||
|
||||
def on_complete(self) -> None:
|
||||
... # wait for acquisition to finish
|
||||
### Base classes and hooks
|
||||
|
||||
def on_unstage(self) -> None:
|
||||
... # release resources
|
||||
```
|
||||
|
||||
Rules of thumb:
|
||||
|
||||
- **Where `ophyd_devices` provides a counterpart to an `ophyd` class, always import the
|
||||
`ophyd_devices` one.** Several ophyd classes are subclassed here to add BEC behaviour, and the plain
|
||||
ophyd version silently loses it. The status classes in `ophyd_devices/utils/psi_device_base_utils.py`
|
||||
— `StatusBase`, `Status`, `DeviceStatus`, `MoveStatus`, `SubscriptionStatus`, `AndStatus` — add
|
||||
timeout diagnostics that report which device and which call is stuck, and the `&` operator for
|
||||
composing statuses. The same module adds BEC-only statuses with no ophyd equivalent
|
||||
(`CompareStatus`, `ExceptionStatus`, `TransitionStatus`, `TaskStatus`), and signals that publish to
|
||||
BEC live in `ophyd_devices/utils/bec_signals.py`.
|
||||
- When lifecycle or business logic is needed, use `PSIDeviceBase` to integrate the control class
|
||||
with BEC's lifecycle, scan information, statuses, and subscriptions. Do not add it solely to
|
||||
expose a collection of signals.
|
||||
- Use the lifecycle hooks provided by the base class rather than replacing its wrappers:
|
||||
`on_init`, `on_connected`, `on_stage`, `on_pre_scan`, `on_trigger`, `on_complete`,
|
||||
`on_kickoff`, `on_unstage`, `on_stop`, and `on_destroy`.
|
||||
- When implementing a device inheriting from `PSIDeviceBase`, copy the entire
|
||||
"Beamline Specific Implementations" section from
|
||||
`ophyd_devices/interfaces/base_classes/psi_device_base.py`, including its separator, all ten
|
||||
hooks, signatures, and docstrings. Keep the hooks together in that order, including unused
|
||||
hooks, so readers can quickly identify the device's business logic.
|
||||
- Put helper methods outside that section under a separate separator, for example:
|
||||
|
||||
```python
|
||||
from ophyd_devices.utils.psi_device_base_utils import DeviceStatus, MoveStatus # yes
|
||||
from ophyd.status import DeviceStatus, MoveStatus # no
|
||||
########################################
|
||||
# Beamline Specific Implementations #
|
||||
########################################
|
||||
|
||||
# All ten on_* hooks belong here.
|
||||
|
||||
########################################
|
||||
# Helper Methods #
|
||||
########################################
|
||||
```
|
||||
|
||||
The same applies to anything re-exported from `ophyd_devices/__init__.py`. Importing straight from
|
||||
`ophyd` stays correct only for what has no counterpart here — `Component`, `EpicsSignal`, `Kind`,
|
||||
`PositionerBase`, and friends.
|
||||
- **Never block.** Long-running work goes through `TaskHandler`, and completion is reported with a
|
||||
`DeviceStatus`. A device that blocks in `stage()` or `trigger()` stalls the whole device server.
|
||||
- **Set `kind` deliberately.** `hinted` signals are recorded by default; `omitted` and `config` are not.
|
||||
A wrong `kind` either loses data or floods every scan file.
|
||||
- **Always implement `stop()`** so the device can be interrupted mid-scan and leaves the hardware safe.
|
||||
- **Emit BEC data through `ophyd_devices/utils/bec_signals.py`** rather than inventing a message shape.
|
||||
- **Check your device against the protocols** in `interfaces/protocols/bec_protocols.py` — they are the
|
||||
contract BEC relies on.
|
||||
- **Add an example configuration** under `ophyd_devices/configs/` when adding a new device family.
|
||||
- When replacing a hook in a subclass, preserve any required parent behavior with `super()`;
|
||||
copied hook stubs must not silently disable an inherited implementation.
|
||||
- Constructors and `on_init()` must not communicate with devices. Use `on_init()` only for local
|
||||
initialization; do not read hardware state or send commands there.
|
||||
- `on_connected()` is the first hook allowed to communicate with devices and send instructions,
|
||||
such as setting hardware defaults. The device server calls it once an enabled device has
|
||||
connected; plain instantiation, unit tests, and `ophyd_test` do not, so call it explicitly in
|
||||
tests that rely on it. It is not a per-scan hook. Use the scan lifecycle hooks for
|
||||
scan-specific instructions and access scan parameters through `self.scan_info.msg`.
|
||||
- Check the relevant protocols when adding or changing a device interface. Read the base-class
|
||||
implementation for hook return semantics; the protocol signatures alone do not describe them.
|
||||
|
||||
### Validating a device configuration
|
||||
### Completion and interruption
|
||||
|
||||
`ophyd_test` statically analyses a device configuration YAML, and can optionally connect to the hardware:
|
||||
- Acquisition, staging, completion, and movement must return promptly to the device server.
|
||||
Represent unfinished work with a status; do not wait, sleep, or poll on the calling thread.
|
||||
- Import statuses from `ophyd_devices.utils.psi_device_base_utils`, including `DeviceStatus`,
|
||||
`MoveStatus`, and `StatusBase`. These provide BEC timeout diagnostics and status composition.
|
||||
Prefer repository helpers whenever a BEC-aware counterpart exists.
|
||||
- Hooks such as `on_stage()` and `on_complete()` may return a status or `None`. Return `None`
|
||||
only when there is no outstanding work. In particular, `complete()` converts `None` into
|
||||
an already-finished status; return a pending status while acquisition or file writing continues.
|
||||
- Register statuses that must fail on interruption with `self.cancel_on_stop(status)`.
|
||||
Implement hardware interruption in `on_stop()` and make repeated calls safe. Cancelling a
|
||||
status does not by itself stop hardware or a background task.
|
||||
- Preserve base-class stop and destroy behavior. Release subscriptions, threads, sockets, and
|
||||
other owned resources in `on_destroy()`; worker code must be able to exit on interruption.
|
||||
|
||||
### Signals and configuration
|
||||
|
||||
- Set signal `kind` deliberately. `normal` and `hinted` signals appear in `read()`; `hinted`
|
||||
also selects default BEC scan readouts. `config` signals appear in `read_configuration()`;
|
||||
`omitted` signals appear in neither. Treat changes to kinds and names as data-interface changes.
|
||||
- Use the signals in `ophyd_devices/utils/bec_signals.py` for BEC live data, progress, and file
|
||||
events. Reuse their message formats instead of publishing custom Redis messages.
|
||||
- Add an example configuration under `ophyd_devices/configs/` for a new reusable device family.
|
||||
- Give each device class a docstring with a useful first-line description. CI uses it to generate
|
||||
`ophyd_devices/devices/device_list.md`; do not edit that generated file by hand.
|
||||
|
||||
### USER_ACCESS
|
||||
|
||||
- Prefer methods over properties for functionality exposed through `USER_ACCESS`.
|
||||
- Give exposed methods verb-based names that describe the operation, such as `set_velocity()`
|
||||
or `get_velocity()`, rather than noun-only names such as `velocity()`.
|
||||
|
||||
## Validation
|
||||
|
||||
|
||||
Add regression tests for bug fixes. New device tests must cover instantiation, the relevant
|
||||
protocol, and safe `stop()` behavior. For asynchronous changes, also exercise completion,
|
||||
failures, and interruption while work is pending.
|
||||
|
||||
Use mocked EPICS and sockets for unit tests. Reuse `get_mock_scan_info` from
|
||||
`ophyd_devices/tests/utils.py` and existing fixtures; use simulation devices when a working device
|
||||
is needed. Keep tests independent of execution order.
|
||||
|
||||
Run the smallest relevant test target first, with `--random-order` when available:
|
||||
|
||||
```bash
|
||||
ophyd_test --config ./ophyd_devices/configs/ophyd_devices_simulation.yaml
|
||||
ophyd_test --config /path/to/beamline_config.yaml --connect --timeout-per-device 30
|
||||
python -m pytest --random-order tests/test_psi_device_base.py
|
||||
```
|
||||
|
||||
Reports are written to `./device_test_reports` by default. Run this before proposing a configuration
|
||||
change for a real beamline.
|
||||
|
||||
## Testing
|
||||
For a full unit test or coverage run:
|
||||
|
||||
```bash
|
||||
python -m pytest --random-order ./tests
|
||||
```
|
||||
|
||||
`--random-order` matches CI and is how order-dependent test pollution gets caught.
|
||||
|
||||
Coverage, as CI measures it:
|
||||
|
||||
```bash
|
||||
coverage run --source=./ophyd_devices --omit=*/ophyd_devices/tests/* -m pytest --random-order ./tests
|
||||
python -m pytest --random-order tests
|
||||
coverage run --source=./ophyd_devices --omit='*/ophyd_devices/tests/*' \
|
||||
-m pytest --random-order tests
|
||||
coverage report
|
||||
```
|
||||
|
||||
**Conventions:**
|
||||
`ophyd_test` writes reports to `./device_test_reports` by default. Use `--connect` only when the
|
||||
user explicitly requests hardware validation and the target is reachable. Report whether device
|
||||
validation used mocks, simulation, or real hardware; include the model and firmware when known.
|
||||
|
||||
- Name files `test_<area>.py` and tests after behaviour — `test_positioner_reports_done_after_move()`,
|
||||
not `test_positioner_3()`.
|
||||
- Mock EPICS and sockets. Use `get_mock_scan_info` from `ophyd_devices/tests/utils.py` and the fixtures
|
||||
in `tests/conftest.py` rather than constructing scan metadata by hand.
|
||||
- Prefer building on the `sim/` devices when you need a working device in a test.
|
||||
- Every new device class needs at least: it instantiates, it satisfies the relevant protocol, and its
|
||||
`stop()` is safe to call.
|
||||
## Running BEC Locally
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
Full service validation needs Redis, usually at `localhost:6379`, and an environment with BEC
|
||||
services installed. Unit tests normally use mocks and need no hardware.
|
||||
|
||||
- Python 3.11+, 4-space indentation, **100-character** line limit.
|
||||
- **Black** and **isort** are the source of truth (settings in `pyproject.toml`). CI fails on any diff:
|
||||
When service management is part of the requested validation, start services and open the client:
|
||||
|
||||
```bash
|
||||
black --line-length=100 --skip-magic-trailing-comma .
|
||||
isort --line-length=100 --profile=black --multi-line=3 --trailing-comma .
|
||||
```
|
||||
```bash
|
||||
bec-server start
|
||||
bec
|
||||
```
|
||||
|
||||
- **Pylint** runs in CI and reports a score; do not introduce new warnings. Beamline-idiomatic names
|
||||
(`scanID`, `RID`, `pointID`, `*_1D`, `*_2D`) are explicitly allowed via `[tool.pylint.basic]`.
|
||||
- `snake_case` for modules, functions, and test files; `PascalCase` for device classes. Device class
|
||||
names should read as the hardware they represent (`SimPositioner`, `PSIMotor`, `DelayGenerator645`).
|
||||
Run the client in another shell. Restart the device server after changing code it has already
|
||||
loaded; otherwise a running session may use stale code.
|
||||
|
||||
## Style And Change Hygiene
|
||||
|
||||
- Use Python 3.11-compatible syntax, four-space indentation, and a 100-character line limit.
|
||||
- Use f-strings and `pathlib`.
|
||||
- **Docstrings are not optional on device classes** — the first line is picked up by the generated
|
||||
device list and is what beamline scientists read when choosing a device.
|
||||
- Type-annotate new public functions and methods; document public APIs.
|
||||
- Follow existing naming and docstring conventions.
|
||||
- Run Black and isort on changed Python files using `pyproject.toml` configuration.
|
||||
- Avoid formatting unrelated files and introducing new Pylint warnings.
|
||||
|
||||
## Development Environment
|
||||
|
||||
Use an existing suitable environment when available. To create one, use Python 3.11 or newer:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python -m pip install -e '.[dev]'
|
||||
python -c "import ophyd_devices; print(ophyd_devices.__file__)"
|
||||
```
|
||||
|
||||
Verify imports resolve to the checkout under test. Separate worktrees need separate editable
|
||||
installations; do not repoint another task's environment. The `dev` extra includes `bec-server`.
|
||||
|
||||
## Platform Notes
|
||||
|
||||
Code must run on **macOS and Linux**. Windows is not supported or tested.
|
||||
Keep code portable across macOS and Linux. Windows is unsupported and untested.
|
||||
|
||||
## Related Repositories
|
||||
## Commit And PR Notes
|
||||
|
||||
- [`bec`](https://github.com/bec-project/bec) — core library and services; `bec_lib` is a direct
|
||||
dependency, and the device server here is driven by `bec_server`.
|
||||
- [`bec_widgets`](https://github.com/bec-project/bec_widgets) — GUI toolkit that displays these devices.
|
||||
- Beamline plugin repositories — beamline-specific devices that do not belong in this shared repository.
|
||||
Branch from `main` for new work. Continue an existing PR on its current branch.
|
||||
|
||||
A device used at exactly one beamline belongs in that beamline's plugin repository. This repository is
|
||||
for hardware support that is reusable across beamlines and facilities.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
The manual PR template lives at `.github/PULL_REQUEST_TEMPLATE/pull_request_template.md`.
|
||||
When writing a description:
|
||||
|
||||
- **Do not commit or push unless explicitly asked to.** Leave the working tree for the human to review.
|
||||
- **Never open, update, or merge a pull request.** Submitting the change is the human contributor's
|
||||
step. An agent's work ends at a reviewed working tree — or at a local commit on a branch, when a
|
||||
commit was explicitly requested.
|
||||
- Branch from `main` with a descriptive name such as `feat/panda-position-capture` or
|
||||
`fix/undulator-timeout`.
|
||||
- **Conventional Commits are mandatory** — `<type>(<scope>): <summary>`, e.g.
|
||||
`fix(psi_motor): report done_moving after limit hit`. Allowed types: `build`, `chore`, `ci`, `docs`,
|
||||
`feat`, `fix`, `perf`, `refactor`, `style`, `test`. `feat` triggers a minor release, `fix` and `perf` a
|
||||
patch release; breaking changes need `!` or a `BREAKING CHANGE:` footer.
|
||||
- Commit messages are parsed by python-semantic-release and become the published `CHANGELOG.md`. Keep
|
||||
them to a single clean subject line.
|
||||
- The pull request itself needs a clear description, linked issues, and test evidence, and for a new
|
||||
device it must state which hardware the device was tested against — or say explicitly that it has
|
||||
only been tested in simulation. Put that in your summary so whoever opens the PR can carry it over.
|
||||
- Lead with the concrete problem and resulting behavior. For a bug fix, explain the trigger and
|
||||
before/after result. Describe the final change for a reviewer who has not followed the work.
|
||||
- Keep detail proportional to the change. Replace prompts and remove unused sections; avoid a
|
||||
file-by-file recap. Put lengthy examples in a `<details>` block and label before/after output.
|
||||
- Use `Closes #123` for resolved issues and `Related to #123` for partial work. Link companion PRs
|
||||
and `bec_docs` updates, including any required merge or deployment order.
|
||||
- Give exact test commands or manual steps and expected outcomes. Separate instructions for
|
||||
reviewers from checks already performed; report results and material limitations honestly.
|
||||
- Explain compatibility changes, affected consumers, defaults, migrations, and remaining limits.
|
||||
Include hardware/simulation validation and configuration-check results where applicable.
|
||||
- State whether documentation was updated or why it is unnecessary. Add design tradeoffs and
|
||||
follow-ups only when they help assess the change. Update the description when scope changes.
|
||||
|
||||
Use Conventional Commit titles: `<type>(<scope>): <summary>`. Allowed types are `build`, `chore`,
|
||||
`ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `style`, and `test`. Mark breaking changes with
|
||||
`!` or a `BREAKING CHANGE:` footer.
|
||||
|
||||
For reviews, use `.github/pull_request_review_template.md`. Separate findings introduced or
|
||||
exposed by the change from inherited issues and optional suggestions. Identify the reviewed
|
||||
revision, give concrete evidence, and state validation limits. Return the review in chat unless
|
||||
posting to GitHub is explicitly requested.
|
||||
|
||||
@@ -1,37 +1,5 @@
|
||||
# CLAUDE.md — `ophyd_devices`
|
||||
# `ophyd_devices`
|
||||
|
||||
@AGENTS.md
|
||||
|
||||
The guidelines above are imported from [`AGENTS.md`](AGENTS.md) (single source of
|
||||
truth). The points that matter most in day-to-day work:
|
||||
|
||||
- **Check for `AGENTS_PERSONAL.md` first.** If it exists, it extends `AGENTS.md` with
|
||||
machine-specific environment setup and takes precedence over the generic venv/pip instructions there.
|
||||
It is untracked and personal — never commit it, and never assume it exists.
|
||||
- **Inherit from `PSIDeviceBase` / `PSIPositionerBase` / `PSIPseudoDeviceBase`**
|
||||
(`ophyd_devices/interfaces/base_classes/`), never from `ophyd.Device` directly — the base classes wire
|
||||
up the subscriptions, `scan_info`, and task/file handling that BEC's device server expects.
|
||||
- **Import the `ophyd_devices` counterpart, never the plain `ophyd` one, wherever one exists.** The
|
||||
status classes in `ophyd_devices/utils/psi_device_base_utils.py` (`StatusBase`, `Status`,
|
||||
`DeviceStatus`, `MoveStatus`, `SubscriptionStatus`, `AndStatus`) subclass ophyd's to add timeout
|
||||
diagnostics and `&` composition, and the module adds BEC-only `CompareStatus`, `ExceptionStatus`,
|
||||
`TransitionStatus`, `TaskStatus`; BEC-publishing signals live in `ophyd_devices/utils/bec_signals.py`.
|
||||
Importing from `ophyd` directly silently drops that behaviour. Plain `ophyd` imports are correct only
|
||||
where there is no counterpart (`Component`, `EpicsSignal`, `Kind`, …).
|
||||
- **Never block the device server.** Long work goes through `TaskHandler` and reports completion with a
|
||||
`DeviceStatus`. Always implement a safe `stop()`.
|
||||
- **Set `kind` deliberately** (`hinted` / `config` / `omitted`) — it decides what lands in the scan file.
|
||||
Emit BEC data through `ophyd_devices/utils/bec_signals.py`, and check the device against the protocols
|
||||
in `interfaces/protocols/bec_protocols.py`.
|
||||
- **Docstring every device class** — the first line feeds the generated
|
||||
`ophyd_devices/devices/device_list.md`, which is CI-generated and must not be hand-edited.
|
||||
- **Tests**: `python -m pytest --random-order ./tests`. Mock EPICS and sockets; build on the `sim/`
|
||||
devices; use `get_mock_scan_info` and the `tests/conftest.py` fixtures.
|
||||
- **Validate configs** with `ophyd_test --config <file.yaml>` (add `--connect` for real hardware).
|
||||
- **Format before finishing**: `black --line-length=100 --skip-magic-trailing-comma .` and
|
||||
`isort --line-length=100 --profile=black --multi-line=3 --trailing-comma .`.
|
||||
- **A device used at only one beamline belongs in that beamline's plugin repo**, not here.
|
||||
- **Do not commit or push unless explicitly asked, and never open a pull request.** If you do commit,
|
||||
write a single Conventional Commits line — it is parsed into the published changelog. Opening the PR
|
||||
is the human's step; leave them the summary and test output they need for it, including whether a new
|
||||
device was tested against real hardware or only in simulation.
|
||||
Follow the shared repository instructions in `AGENTS.md`, including its local-overlay guidance.
|
||||
|
||||
Reference in New Issue
Block a user