Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New signal typing #594

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fd5e699
WIP
coretl Sep 24, 2024
2e9dd61
Slightly improve the connect signature
coretl Sep 25, 2024
2f6d171
Update src/ophyd_async/core/_device.py
coretl Sep 26, 2024
fdb4504
Update src/ophyd_async/core/_table.py
coretl Sep 26, 2024
c70d449
Update src/ophyd_async/epics/signal/_signal.py
coretl Sep 26, 2024
4ecce34
Merge remote-tracking branch 'origin/signal-typing' into signal-typing
coretl Sep 26, 2024
4a5c27f
Convert p4p and improve ca to match
coretl Sep 26, 2024
7855b78
First draft of PviConnector
coretl Sep 27, 2024
f25742f
Mostly move to Connector ideas
coretl Oct 4, 2024
2923ef2
Most tests working
coretl Oct 8, 2024
09bcfab
Convert DeviceConnector -> DeviceBackend
coretl Oct 9, 2024
0c36af8
Finish up conversion back to Backend
coretl Oct 11, 2024
ad2fc8b
Fix some shape tests
coretl Oct 14, 2024
9247091
Merge remote-tracking branch 'origin/main' into signal-typing
coretl Oct 14, 2024
e393198
Fix Tango tests to work with new types
coretl Oct 14, 2024
0d5409e
Move DeviceFiller to core and use it in Tango
coretl Oct 15, 2024
f7c42c2
Un-preview the ruff formatting
coretl Oct 15, 2024
8753b38
Add PLC2701 ruff rule
coretl Oct 15, 2024
b87feee
fix types in test_device_save_loader (#614)
jsouter Oct 16, 2024
1aa7e3b
WIP
coretl Oct 17, 2024
5ce2138
Merge remote-tracking branch 'origin/signal-typing' into signal-typin…
coretl Oct 17, 2024
3d88212
Converted back to Connector, but with SignalBackend
coretl Oct 17, 2024
bb1759d
Fixup pvi tests and temp skip tango auto attrs
coretl Oct 17, 2024
412d78a
Fix lint
coretl Oct 17, 2024
937a7c5
Minimize diff
coretl Oct 18, 2024
04662e8
Improve error message for get_dtype
coretl Oct 18, 2024
086433f
Add ADR for signal types
coretl Oct 18, 2024
361cb52
Fix lint
coretl Oct 18, 2024
1f75ea0
Address PR comments
coretl Oct 22, 2024
1f288ee
Fix timeout in CaSignalBackend.put
coretl Oct 22, 2024
be6d103
Changed parenting to happen on setattr/item and naming on set_name
coretl Oct 22, 2024
d9eb60c
Fix naming of PVI/Tango devices
coretl Oct 22, 2024
e2df324
Don't set parent of child Devices with leading underscores
coretl Oct 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions docs/explanations/decisions/0008-signal-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 8. Settle on Signal Types
Date: 2024-10-18

## Status

Accepted

## Context

At present, soft Signals allow any sort of datatype, while CA, PVA, Tango restrict these to what the control system allows. This means that some soft signals when `describe()` is called on them will give `dtype=object` which is not understood by downstream tools. It also means that load/save will not necessarily understand how to serialize results. Finally we now require `dtype_numpy` for tiled, so arbitrary object types are not suitable even if they are serializable. We should restrict the datatypes allowed in Signals to objects that are serializable and are sensible to add support for in downstream tools.

## Decision

We will allow the following:
- Primitives:
- `bool`
- `int`
- `float`
- `str`
- Enums:
- `StrictEnum` subclass which will be checked to have the same members as the CS
- `SubsetEnum` subclass which will be checked to be a subset of the CS members
- 1D arrays:
- `Array1D[np.bool_]`
- `Array1D[np.int8]`
- `Array1D[np.uint8]`
- `Array1D[np.int16]`
- `Array1D[np.uint16]`
- `Array1D[np.int32]`
- `Array1D[np.uint32]`
- `Array1D[np.int64]`
- `Array1D[np.uint64]`
- `Array1D[np.float32]`
- `Array1D[np.float64]`
- `Sequence[str]`
- `Sequence[MyEnum]` where `MyEnum` is a subclass of `StrictEnum` or `SubsetEnum`
- Specific structures:
- `np.ndarray` to represent arrays where dimensionality and dtype can change and must be read from CS
- `Table` subclass (which is a pydantic `BaseModel`) where all members are 1D arrays

## Consequences

Clients will be expected to understand:
- Python primitives (with Enums serializing as strings)
- Numpy arrays
- Pydantic BaseModels

All of the above have sensible `dtype_numpy` fields, but `Table` will give a structured row-wise `dtype_numpy`, while the data will be serialized in a column-wise fashion.
8 changes: 4 additions & 4 deletions docs/how-to/make-a-simple-device.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. note::

Ophyd async is included on a provisional basis until the v1.0 release and
Ophyd async is included on a provisional basis until the v1.0 release and
may change API on minor release numbers before then

Make a Simple Device
Expand Down Expand Up @@ -31,7 +31,7 @@ its Python type, which could be:
- A primitive (`str`, `int`, `float`)
- An array (`numpy.typing.NDArray` ie. ``numpy.typing.NDArray[numpy.uint16]`` or ``Sequence[str]``)
- An enum (`enum.Enum`) which **must** also extend `str`
- `str` and ``EnumClass(str, Enum)`` are the only valid ``datatype`` for an enumerated signal.
- `str` and ``EnumClass(StrictEnum)`` are the only valid ``datatype`` for an enumerated signal.

The rest of the arguments are PV connection information, in this case the PV suffix.

Expand All @@ -45,7 +45,7 @@ Finally `super().__init__() <StandardReadable>` is called with:
without renaming

All signals passed into this init method will be monitored between ``stage()``
and ``unstage()`` and their cached values returned on ``read()`` and
and ``unstage()`` and their cached values returned on ``read()`` and
``read_configuration()`` for perfomance.

Movable
Expand All @@ -64,7 +64,7 @@ informing watchers of the progress. When it gets to the requested value it
completes. This co-routine is wrapped in a timeout handler, and passed to an
`AsyncStatus` which will start executing it as soon as the Run Engine adds a
callback to it. The ``stop()`` method then pokes a PV if the move needs to be
interrupted.
interrupted.

Assembly
--------
Expand Down
30 changes: 14 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ classifiers = [
description = "Asynchronous Bluesky hardware abstraction code, compatible with control systems like EPICS and Tango"
dependencies = [
"networkx>=2.0",
"numpy<2.0.0",
"numpy",
coretl marked this conversation as resolved.
Show resolved Hide resolved
"packaging",
"pint",
"bluesky>=1.13.0a3",
"event_model",
"p4p",
"bluesky @ git+https://github.com/bluesky/bluesky@main",
"event-model @ git+https://github.com/bluesky/event-model@main",
coretl marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do a patch release of event model soon which allows |

https://github.com/bluesky/ophyd-async/pull/594/files#r1784069033

"p4p>=4.2.0a3",
"pyyaml",
"colorlog",
"pydantic>=2.0",
Expand All @@ -39,10 +39,6 @@ dev = [
"ophyd_async[sim]",
"ophyd_async[ca]",
"ophyd_async[tango]",
"black",
"flake8",
"flake8-isort",
"Flake8-pyproject",
"inflection",
"ipython",
"ipywidgets",
Expand Down Expand Up @@ -150,15 +146,17 @@ commands =
src = ["src", "tests", "system_tests"]
line-length = 88
lint.select = [
"B", # flake8-bugbear - https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
"C4", # flake8-comprehensions - https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4
"E", # pycodestyle errors - https://docs.astral.sh/ruff/rules/#error-e
"F", # pyflakes rules - https://docs.astral.sh/ruff/rules/#pyflakes-f
"W", # pycodestyle warnings - https://docs.astral.sh/ruff/rules/#warning-w
"I", # isort - https://docs.astral.sh/ruff/rules/#isort-i
"UP", # pyupgrade - https://docs.astral.sh/ruff/rules/#pyupgrade-up
"SLF", # self - https://docs.astral.sh/ruff/settings/#lintflake8-self
"B", # flake8-bugbear - https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
"C4", # flake8-comprehensions - https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4
"E", # pycodestyle errors - https://docs.astral.sh/ruff/rules/#error-e
"F", # pyflakes rules - https://docs.astral.sh/ruff/rules/#pyflakes-f
"W", # pycodestyle warnings - https://docs.astral.sh/ruff/rules/#warning-w
"I", # isort - https://docs.astral.sh/ruff/rules/#isort-i
"UP", # pyupgrade - https://docs.astral.sh/ruff/rules/#pyupgrade-up
"SLF", # self - https://docs.astral.sh/ruff/settings/#lintflake8-self
"PLC2701", # private import - https://docs.astral.sh/ruff/rules/import-private-name/
]
lint.preview = true # so that preview mode PLC2701 is enabled

[tool.ruff.lint.per-file-ignores]
# By default, private member access is allowed in tests
Expand Down
27 changes: 19 additions & 8 deletions src/ophyd_async/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
StandardDetector,
TriggerInfo,
)
from ._device import Device, DeviceCollector, DeviceVector
from ._device import Device, DeviceCollector, DeviceConnector, DeviceVector
from ._device_filler import DeviceFiller
from ._device_save_loader import (
all_at_once,
get_signal_values,
Expand Down Expand Up @@ -62,9 +63,11 @@
wait_for_value,
)
from ._signal_backend import (
RuntimeSubsetEnum,
Array1D,
SignalBackend,
SubsetEnum,
SignalDatatype,
SignalDatatypeT,
make_datakey,
)
from ._soft_signal_backend import SignalMetadata, SoftSignalBackend
from ._status import AsyncStatus, WatchableAsyncStatus, completed_status
Expand All @@ -73,14 +76,16 @@
CALCULATE_TIMEOUT,
DEFAULT_TIMEOUT,
CalculatableTimeout,
Callback,
NotConnected,
ReadingValueCallback,
StrictEnum,
SubsetEnum,
T,
WatcherUpdate,
get_dtype,
get_enum_cls,
get_unique,
in_micros,
is_pydantic_model,
wait_for_connection,
)

Expand All @@ -91,8 +96,10 @@
"StandardDetector",
"TriggerInfo",
"Device",
"DeviceConnector",
"DeviceCollector",
"DeviceVector",
"DeviceFiller",
"all_at_once",
"get_signal_values",
"load_device",
Expand Down Expand Up @@ -146,25 +153,29 @@
"soft_signal_r_and_setter",
"soft_signal_rw",
"wait_for_value",
"RuntimeSubsetEnum",
"Array1D",
"SignalBackend",
"make_datakey",
"StrictEnum",
"SubsetEnum",
"SignalDatatype",
"SignalDatatypeT",
"SignalMetadata",
"SoftSignalBackend",
"AsyncStatus",
"WatchableAsyncStatus",
"DEFAULT_TIMEOUT",
"CalculatableTimeout",
"Callback",
"CALCULATE_TIMEOUT",
"NotConnected",
"ReadingValueCallback",
"Table",
"T",
"WatcherUpdate",
"get_dtype",
"get_enum_cls",
"get_unique",
"in_micros",
"is_pydantic_model",
"wait_for_connection",
"completed_status",
]
15 changes: 5 additions & 10 deletions src/ophyd_async/core/_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@
import time
from abc import ABC, abstractmethod
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Iterator, Sequence
from enum import Enum
from functools import cached_property
from typing import (
Generic,
)

from bluesky.protocols import (
Collectable,
Expand All @@ -23,14 +19,14 @@
from event_model import DataKey
from pydantic import BaseModel, Field, NonNegativeInt, computed_field

from ._device import Device
from ._device import Device, DeviceConnector
from ._protocol import AsyncConfigurable, AsyncReadable
from ._signal import SignalR
from ._status import AsyncStatus, WatchableAsyncStatus
from ._utils import DEFAULT_TIMEOUT, T, WatcherUpdate, merge_gathered_dicts
from ._utils import DEFAULT_TIMEOUT, StrictEnum, WatcherUpdate, merge_gathered_dicts


class DetectorTrigger(str, Enum):
class DetectorTrigger(StrictEnum):
"""Type of mechanism for triggering a detector to take frames"""

#: Detector generates internal trigger for given rate
Expand Down Expand Up @@ -172,7 +168,6 @@ class StandardDetector(
Flyable,
Collectable,
WritesStreamAssets,
Generic[T],
):
"""
Useful detector base class for step and fly scanning detectors.
Expand All @@ -185,6 +180,7 @@ def __init__(
writer: DetectorWriter,
config_sigs: Sequence[SignalR] = (),
name: str = "",
connector: DeviceConnector | None = None,
) -> None:
"""
Constructor
Expand Down Expand Up @@ -213,8 +209,7 @@ def __init__(
self._completable_frames: int = 0
self._number_of_triggers_iter: Iterator[int] | None = None
self._initial_frame: int = 0

super().__init__(name)
super().__init__(name, connector=connector)

@property
def controller(self) -> DetectorController:
Expand Down
Loading
Loading