Source code for apstools.devices.simulated_controllers

"""
Simulate process controllers as positioners using EPICS records.

.. autosummary::

    ~SimulatedSwaitControllerPositioner
    ~SimulatedTransformControllerPositioner
"""

import time

from ophyd import FormattedComponent as FC

from ..synApps import SwaitRecord
from ..synApps import TransformRecord
from . import PVPositionerSoftDoneWithStop


[docs]class SimulatedSwaitControllerPositioner(PVPositionerSoftDoneWithStop): """ Simulated process controller as positioner with EPICS swait record. The swait record completes the feedback loop, computing the next simulated controller reading. Example with ``swait`` record:: controller = SimulatedSwaitControllerPositioner( "", name="controller", loop_pv="gp:userCalc1", ) controller.wait_for_connection() controller.setup(25) .. autosummary:: ~setup """ loop = FC(SwaitRecord, "{loop_pv}", kind="config") def __init__(self, *args, loop_pv="", **kwargs): if len(loop_pv.strip()) == 0: raise ValueError("Must supply a value for 'loop_pv'.") self.loop_pv = loop_pv kwargs["readback_pv"] = f"{loop_pv}.VAL" kwargs["setpoint_pv"] = f"{loop_pv}.B" super().__init__(*args, **kwargs)
[docs] def setup( self, setpoint, label="controller", noise=1, period="1 second", max_change=1, tolerance=1, ): """ Configure the swait record as a process controller. """ self.tolerance.put(tolerance) swait = self.loop swait.reset() # remove any prior configuration time.sleep(2.0 / 60) # short pause for IOC processing swait.description.put(label) swait.channels.A.input_value.put(setpoint) # readback swait.channels.A.input_pv.put(swait.calculated_value.pvname) swait.channels.B.input_value.put(setpoint) # setpoint swait.channels.C.input_value.put(noise) swait.channels.D.input_value.put(max_change) swait.scanning_rate.put(period) swait.precision.put(3) swait.calculated_value.put(setpoint) # preset initial value swait.calculation.put("A+max(-D,min(D,(B-A)))+C*(RNDM-0.5)")
[docs]class SimulatedTransformControllerPositioner(PVPositionerSoftDoneWithStop): """ Simulated process controller as positioner with EPICS transform record. The transform record completes the feedback loop, computing the next simulated controller reading and reporting if the readback is "in position". Example with ``transform`` record:: controller = SimulatedTransformControllerPositioner( "", name="controller", loop_pv="gp:userTran1", ) controller.wait_for_connection() controller.setup(25) .. autosummary:: ~setup """ loop = FC(TransformRecord, "{loop_pv}", kind="config") def __init__(self, *args, loop_pv="", **kwargs): if len(loop_pv.strip()) == 0: raise ValueError("Must supply a value for 'loop_pv'.") self.loop_pv = loop_pv kwargs["readback_pv"] = f"{loop_pv}.H" kwargs["setpoint_pv"] = f"{loop_pv}.B" super().__init__(*args, **kwargs) self.following_error = self.loop.channels.E.current_value
[docs] def setup( self, setpoint, label="controller", noise=2, period="1 second", max_change=2, tolerance=1, ): """ Configure the transform record as a process controller. """ self.wait_for_connection() self.tolerance.put(tolerance) transform = self.loop transform.reset() # remove any prior configuration time.sleep(2.0 / 60) # short pause for IOC processing transform.description.put(label) transform.channels.A.comment.put("last readback") transform.channels.A.current_value.put(setpoint) # readback transform.channels.A.input_pv.put(transform.channels.H.current_value.pvname) transform.channels.B.comment.put("setpoint") transform.channels.B.current_value.put(setpoint) # setpoint transform.channels.C.comment.put("noise level") transform.channels.C.current_value.put(noise) transform.channels.D.comment.put("max_change") transform.channels.D.current_value.put(max_change) transform.channels.E.comment.put("following error") transform.channels.E.expression.put("B-A") transform.channels.F.comment.put("step") transform.channels.F.expression.put("max(-D,min(D,E))") transform.channels.G.comment.put("noise") transform.channels.G.expression.put("C*(RNDM-0.5)") transform.channels.H.comment.put("readback") transform.channels.H.current_value.put(setpoint) # preset initial value transform.channels.H.expression.put("A+F+G") transform.channels.I.comment.put("tolerance") transform.channels.I.current_value.put(tolerance) transform.channels.J.comment.put("in position") transform.channels.J.expression.put("abs(H-B)<=I") transform.precision.put(3) transform.scanning_rate.put(period)