Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
100 changes: 100 additions & 0 deletions docs/how-to/devices/add-a-file-event-signal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
related:
- title: BEC Signals for Custom Devices
url: learn/devices/bec-signals.md
- title: File writing
url: learn/file-writer/introduction.md
- title: ReadoutPriority in BEC
url: learn/devices/readout-priority.md
---

# Add a File Event Signal to a Custom Device

!!! Info "Overview"
Add a `FileEventSignal` to a custom ophyd device when the real measurement data is written to an external file and BEC must track the output path and completion state.

## Prerequisites

- You already have a custom device class in Python.
- Your device writes scan data to a file such as HDF5.
- Your device knows the final output path before or during acquisition.
- Your acquisition flow exposes a completion state or status callback.

!!! learn "[Learn about BEC signal classes](../../learn/devices/bec-signals.md){ data-preview }"

## 1. Declare the signal on the device class

Add `FileEventSignal` as a component on your device class:

```python
from ophyd import Component as Cpt
from ophyd_devices import FileEventSignal
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase


class MyDetector(PSIDeviceBase):
file_event = Cpt(FileEventSignal, name="file_event")
```

## 2. Emit the initial file event before acquisition starts

As soon as the final output path is known, publish an initial file event with `done=False`.

This is the same pattern used in `csaxs_bec/devices/jungfraujoch/eiger.py`:

```python
self.file_event.put(
file_path=self._full_path,
done=False,
successful=False,
hinted_h5_entries={"data": "entry/data/data"},
)
```

In many devices this is a good fit for `on_stage()`.

## 3. Emit the final file event when acquisition completes

When the asynchronous acquisition finishes, publish a second file event with the resolved completion status.

```python
def _file_event_callback(self, status: DeviceStatus) -> None:
self.file_event.put(
file_path=self._full_path,
done=status.done,
successful=status.success,
hinted_h5_entries={"data": "entry/data/data"},
)
```

Attach this callback to the status object returned by your asynchronous acquisition or completion logic.

## 4. Provide HDF5 dataset hints when applicable

For HDF5-based detectors, set `hinted_h5_entries` so downstream BEC components know where the primary data lives inside the file.

Example:

```python
hinted_h5_entries={"data": "entry/data/data"}
Comment thread
cappel89 marked this conversation as resolved.
```

This is especially important for downstream file linking and data discovery.

## 5. Verify the file event flow

Run a short acquisition and confirm that:

- the initial file event is emitted with the expected `file_path`
- the final file event is emitted after completion
- `successful` reflects the actual acquisition outcome
- the path points to the final file, not a temporary staging location

If your file format or downstream consumer needs extra context, include fields such as `file_type` or `metadata`.

## 6. Use the file event in BEC
With the file event signal in place, BEC can now track the produced file and its completion state alongside the rest of the acquisition. For more information on how to use files in BEC and how to link them, see the [File Writer documentation](../../learn/file-writer/introduction.md).


!!! success "Congratulations!"
You have successfully added a `FileEventSignal` to a custom device. BEC can now track the produced file and its completion state alongside the rest of the acquisition.
100 changes: 100 additions & 0 deletions docs/how-to/devices/add-a-preview-signal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
related:
- title: BEC Signals for Custom Devices
url: learn/devices/bec-signals.md
- title: Introduction to ophyd
url: learn/devices/introduction-to-ophyd.md
- title: ReadoutPriority in BEC
url: learn/devices/readout-priority.md
---

# Add a Preview Signal to a Custom Device
Comment thread
wakonig marked this conversation as resolved.

!!! Info "Overview"
Add a `PreviewSignal` to a custom ophyd device when you want BEC to forward live 1D or 2D preview data such as images, spectra, or monitor streams.

## Prerequisites

- You already have a custom device class in Python.
- Your device receives preview-like data from a callback, background thread, stream, or external client.
- You know whether the preview data is 1D or 2D.

!!! learn "[Learn about BEC signal classes](../../learn/devices/bec-signals.md){ data-preview }"

## 1. Declare the signal on the device class

Add `PreviewSignal` as a component on your device class.

Use `ndim=2` for images:

```python
from ophyd import Component as Cpt
from ophyd_devices import PreviewSignal
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase


class MyDetector(PSIDeviceBase):
preview_image = Cpt(PreviewSignal, name="preview_image", ndim=2)
```

Use `ndim=1` instead for line-like previews such as spectra.

## 2. Connect it to your preview callback

When your device receives new preview data, extract the payload first and then publish it with `put(...)`.

`PreviewSignal` accepts preview data in multiple forms:

- a Python `list`
- a NumPy array
- a fully constructed `DevicePreviewMessage`


```python
def _preview_callback(self, message: dict) -> None:
if message.get("type", "") != "image":
return

data = message.get("data", {}).get("default")
if data is None:
return

self.preview_image.put(data)
```

You first receive or assemble the preview payload in your callback or stream handler, and only then set it on the signal with `put(...)`.

If you pass a list or NumPy array, `PreviewSignal` wraps it into the BEC preview message type for you. If you already have a `DevicePreviewMessage`, you can pass that directly.

!!! warning "Stay aware of the data rate"
When connecting a `PreviewSignal`, make sure that the incoming preview data rate is not too high. The purpose of a preview is to provide a lightweight stream that can be forwarded to GUIs and clients without overwhelming the network or BEC. If your preview data arrives at a very high rate, consider adding throttling or decimation logic in your callback before sending it to the signal.

## 3. Adjust orientation if needed

If your upstream image arrives in a different orientation than you want to show in BEC, configure the signal declaration with `transpose` or `num_rotation_90`.

Example:

```python
preview_image = Cpt(
PreviewSignal,
name="preview_image",
ndim=2,
transpose=True,
num_rotation_90=1,
)
```

## 4. Verify the preview in BEC

Start the device, trigger the upstream preview source, and confirm that the preview appears in the relevant BEC client or GUI.

If nothing appears:

- verify that your callback is being called
- verify that `data` is not `None`
- verify that `ndim` matches the actual payload shape
- verify that the device is configured with a suitable `readoutPriority`

!!! success "Congratulations!"
You have successfully added a `PreviewSignal` to a custom device. BEC can now forward your live preview stream independently of the final stored data.
100 changes: 100 additions & 0 deletions docs/how-to/devices/add-a-progress-signal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
related:
- title: BEC Signals for Custom Devices
url: learn/devices/bec-signals.md
- title: ReadoutPriority in BEC
url: learn/devices/readout-priority.md
- title: Introduction to ophyd
url: learn/devices/introduction-to-ophyd.md
---

# Add a Progress Signal to a Custom Device

!!! Info "Overview"
Add a `ProgressSignal` to a custom ophyd device when you want BEC to track scan progress from that device, for example while data is being acquired or processed asynchronously.

## Prerequisites

- You already have a custom device class in Python.
- Your device has a natural progress measure such as completed triggers, completed frames, or processed points.
- You know the current value and the expected maximum value during the operation.

!!! learn "[Learn about BEC signal classes](../../learn/devices/bec-signals.md){ data-preview }"

## 1. Declare the signal on the device class

Add `ProgressSignal` as a component on your device class:

```python
from ophyd import Component as Cpt, Device
from ophyd_devices import ProgressSignal


class MyDetector(Device):
progress = Cpt(ProgressSignal, name="progress")
```

This creates one signal that emits BEC progress messages.

## 2. Decide what progress means for the device

Choose one quantity that moves toward completion in a clear way.

Typical examples are:

- completed frames out of total frames
- completed scan points out of total points
- processed events out of total events

The most common pattern is to send:

- the current value
- the maximum value
- whether the operation is done

## 3. Update the signal during runtime

When the device makes progress, send a progress update with `put(...)`.

Example:

```python
self.progress.put(value=25, max_value=100, done=False)
```

When the operation is finished:

```python
self.progress.put(value=100, max_value=100, done=True)
```

## 4. Connect it to your callback or worker loop

`ProgressSignal` is usually updated from a callback, subscription, worker thread, or polling loop.

Typical pattern:

```python
def _update_progress(self, completed, total):
self.progress.put(
value=completed,
max_value=total,
done=completed >= total,
)
```

This keeps the progress signal close to the logic that already knows how much work has been completed.

## 5. Verify the progress updates

Run a short acquisition and confirm that:

- the device sends progress updates while work is ongoing
- `value` increases in the expected direction
- `max_value` is stable and meaningful
- `done` becomes `true` when the operation finishes

If progress does not appear in BEC, first check whether the callback or worker loop that computes progress is actually running.

!!! success "Congratulations!"
You have successfully added a `ProgressSignal` to a custom device. BEC can now track the device's runtime progress during the scan.
Loading
Loading