Skip to content

Add ProcessedSignal to ophyd devices#188

Open
cappel89 wants to merge 8 commits intomainfrom
feat/add_processed_signal
Open

Add ProcessedSignal to ophyd devices#188
cappel89 wants to merge 8 commits intomainfrom
feat/add_processed_signal

Conversation

@cappel89
Copy link
Member

@cappel89 cappel89 commented Mar 6, 2026

Summary

This PR introduces an implementation for a ProcessedSignal as well as an alternative to write PseudoMotors in BEC.

ProcessedSignals

ProcessedSignals can either be implemented by having a fully defined config passed to the initialization or similar to the implementation here, where string arguments are used to look for the respective signal/devices in the devicemanager of BEC. Device references must be given as dotted names, and the signal will subscribe to the _default_sub of the given object. The compute method will receive the objects and can in return compute/calculate whatever it desires with the given object. However, upon connecting we will test that the return_value matches the type specified in the model for the signal.

Virtual Motors

Using the processed signals, a PSIPseudoMotor base class is introduced. It relies on the signals and uses them to generate a pseudo motor interface with at least readback/user_readback, setpoint/user_setpoint and motor_is_moving. A center and width implementation is introduced to devices, whereas an example for an alternative integration for a center based on sub-signals is shown here.

Note

  • Test will be added later today. Code is already ready to review.

To test

You may add the following devices to your demo_config.yaml to test the implementation.

sl5width:
  readoutPriority: baseline
  deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitWidth
  deviceConfig:
    left_slit: sl5trxi
    right_slit: sl5trxo
  enabled: true
  readOnly: false
  needs:
    - sl5trxi
    - sl5trxo

sl5center:
  readoutPriority: baseline
  deviceClass: ophyd_devices.devices.virtual_slit.VirtualSlitCenter
  deviceConfig:
    left_slit: sl5trxi
    right_slit: sl5trxo
  enabled: true
  readOnly: false
  needs:
    - sl5trxi
    - sl5trxo

slitcenter:
  readoutPriority: baseline
  deviceClass: ophyd_devices.sim.sim_test_devices.VirtualSlitCenter
  enabled: true
  readOnly: false

pseudosignal:
  readoutPriority: monitored
  deviceClass: ophyd_devices.sim.sim_test_devices.BECPseudoSignal
  deviceConfig:
    signal_1: bpm4i
    signal_2: bpm5i
  enabled: true
  readOnly: false
  needs:
    - bpm4i
    - bpm5i

pseudodevicesignal:
  readoutPriority: monitored
  deviceClass: ophyd_devices.sim.sim_test_devices.BECPseudoSignal
  deviceConfig:
    signal_1: samx.readback
    signal_2: samy.readback
  enabled: true
  readOnly: false
  needs:
    - samx
    - samy

Copilot AI review requested due to automatic review settings March 6, 2026 13:13
@cappel89 cappel89 self-assigned this Mar 6, 2026
@cappel89 cappel89 marked this pull request as draft March 6, 2026 13:13
@codecov
Copy link

codecov bot commented Mar 6, 2026

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new BECProcessedSignal class that computes its value based on signals from other devices in BEC's device manager, along with supporting changes to sim signals to properly trigger subscription callbacks.

Changes:

  • Added ophyd_devices/devices/processed_signal.py with ProcessedSignalModel (Pydantic config model), DeviceSignalMapping, and BECProcessedSignal (a read-only ophyd signal that subscribes to other device signals and computes a derived value via a user-defined function).
  • Modified SetableSignal.get() and ReadOnlySignal.get() in sim_signals.py to update _readback and fire _run_subs on every get() call, enabling subscription-based updates needed by BECProcessedSignal.
  • Removed an unused BECDeviceBase import and a commented-out line from sim_signals.py.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
ophyd_devices/devices/processed_signal.py New module defining BECProcessedSignal — a signal that computes its value from other device signals via a configurable model. Includes validation, subscription management, and an example __main__ block.
ophyd_devices/sim/sim_signals.py Updates SetableSignal.get() and ReadOnlySignal.get() to properly set _readback and trigger _run_subs callbacks; removes unused import and dead code.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@cappel89 cappel89 force-pushed the feat/add_processed_signal branch 5 times, most recently from ec810a5 to 6192dad Compare March 12, 2026 15:03
@cappel89 cappel89 force-pushed the feat/add_processed_signal branch from 6192dad to 471286c Compare March 17, 2026 13:06
@cappel89 cappel89 force-pushed the feat/add_processed_signal branch from 471286c to b92e6ca Compare March 17, 2026 14:10
Copy link
Contributor

@d-perl d-perl left a comment

Choose a reason for hiding this comment

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

This looks great! I am basically okay with this implementation, I think it should work well, but I had a crazy idea about how to change it a bit which I would like your feedback on:

  • is it important that the model can be dynamically changed? if not, I would consider just requiring it on init and getting rid of set_model, but if it's important, then set_model should refresh the things set up in the connection (see comments in code)
    • to make that work with the pseudopositioner I think it should be possible if the model devices could be a dict[str, str] | Callable[[BECProcessedSignal], dict[str, str]] which would be called explicitly with the signal self before get_device_objects_from_device_manager in wait_for_connection
    • this could then do signal.parent.get_positioner_objects in PSIPseudoMotorBase to keep the simple reference by device name in the concrete subclasses

I think the tradeoff is that creating base classes like the PSIPseudoMotorBase becomes a bit more complicated, but we remove some potential sources of runtime errors and making the subclasses like the VirtualSlitCenter would be even a tiny bit simpler

@cappel89 cappel89 marked this pull request as ready for review March 18, 2026 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants