예제 #1
0
class SimpleIOC(PVGroup):
    """
    An IOC with three uncoupled read/writable PVs

    Scalar PVs
    ----------
    A (int)
    B (float)

    Vectors PVs
    -----------
    C (vector of int)
    """
    A = pvproperty(value=1, doc='An integer')
    B = pvproperty(value=2.0, doc='A float')
    C = pvproperty(value=[1, 2, 3], doc='An array of integers')
예제 #2
0
    def create_pvproperty(attr, record):
        startup_fields[attr] = {}
        default_value = default_values.get(record.record_type, 0)
        if record.record_type == 'waveform':
            ftvl = record.fields.get('FTVL', 'SHORT')
            nelm = int(record.fields.get('NELM', 1))
            default_value = default_value[ftvl]
            if ftvl != 'CHAR':
                default_value = default_value * nelm

        if 'VAL' in record.fields:
            value = type(default_value)(record.fields['VAL'])
        else:
            value = default_value

        prop = pvproperty(
            name=record.pvname,
            value=value,
            record=record.record_type,
        )

        for field_name, value in record.fields.items():
            if field_name != 'VAL':
                startup_fields[attr][field_name] = value

        for alias_idx, alias in enumerate(record.aliases):
            logger.debug('TODO - aliases: %s %s', record, alias)

        class_dict[attr] = prop
        return prop
예제 #3
0
class IOInterruptIOC(PVGroup):
    keypress = pvproperty(value=[''])

    # NOTE the decorator used here:
    @keypress.startup
    async def keypress(self, instance, async_lib):
        # This method will be called when the server starts up.
        print('* keypress method called at server startup')
        queue = async_lib.ThreadsafeQueue()

        # Start a separate thread that monitors keyboard input, telling it to
        # put new values into our async-friendly queue
        thread = threading.Thread(target=start_io_interrupt_monitor,
                                  daemon=True,
                                  kwargs=dict(new_value_callback=queue.put))
        thread.start()

        # Loop and grab items from the queue one at a time
        while True:
            value = await queue.async_get()
            print(f'Saw new value on async side: {value!r}')

            # Propagate the keypress to the EPICS PV, triggering any monitors
            # along the way
            await self.keypress.write(str(value))
예제 #4
0
class FakeMotor(PVGroup):
    motor = pvproperty(value=0.0, name='', record='motor',
                       precision=3)

    def __init__(self, *args,
                 velocity=0.1,
                 precision=3,
                 acceleration=1.0,
                 resolution=1e-6,
                 tick_rate_hz=10.,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self._have_new_position = False
        self.tick_rate_hz = tick_rate_hz
        self.defaults = {
            'velocity': velocity,
            'precision': precision,
            'acceleration': acceleration,
            'resolution': resolution,
        }

    @motor.startup
    async def motor(self, instance, async_lib):
        # Start the simulator:
        await motor_record_simulator(
            self.motor, async_lib,
            tick_rate_hz=self.tick_rate_hz,
        )
예제 #5
0
class RandomWalkIOC(PVGroup):
    dt = pvproperty(value=3.0)
    x = pvproperty(value=0.0)

    @x.startup
    async def x(self, instance, async_lib):
        'Periodically update the value'
        while True:
            # compute next value
            x = self.x.value + 2 * random.random() - 1

            # update the ChannelData instance and notify any subscribers
            await instance.write(value=x)

            # Let the async library wait for the next iteration
            await async_lib.library.sleep(self.dt.value)
예제 #6
0
class DynamicLVGroup(LVGroup):
    device_list_message_cls = None
    device_cls = None
    devices = pvproperty(value=[], dtype=ChannelType.STRING, max_length=10000)

    async def update(self):
        device_names = (await
                        self.parent.get(self.device_list_message_cls())).data
        newpvs = {}
        for name in device_names:
            device = self.device_cls(name, parent=self)
            newpvs.update({
                f'{self.prefix}{name}.{value.pvspec.name}': value
                for key, value in device.attr_pvdb.items()
                if f'{self.prefix}{name}.{key}' not in self.pvdb
            })
            # newpvs.update({f'{self.prefix}{name}.{key}': value for key, value in device.attr_pvdb.items()
            #                if f'{self.prefix}{name}.{key}' not in self.pvdb})
        self.pvdb.update(newpvs)
        return newpvs

    @devices.getter
    async def devices(self, instance):
        await self.update()
        return list(self.pvdb.keys())
예제 #7
0
파일: motor.py 프로젝트: pcdshub/sim-ioc
class Motor(PVGroup):
    motor = pvproperty(value=0.0, name='', record='motor', precision=3)

    def __init__(self,
                 *args,
                 position=0.0,
                 velocity=1.0,
                 precision=3,
                 acceleration=1.0,
                 resolution=1e-6,
                 user_limits=(0.0, 100.0),
                 tick_rate_hz=10.,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self.tick_rate_hz = tick_rate_hz
        self.defaults = {
            "position": position,
            "velocity": velocity,
            "precision": precision,
            "acceleration": acceleration,
            "resolution": resolution,
            "user_limits": tuple(user_limits),
        }

    @motor.startup
    async def motor(self, instance, async_lib):
        # Start the simulator:
        await motor_record_simulator(
            self.motor,
            async_lib,
            self.defaults,
            tick_rate_hz=self.tick_rate_hz,
        )
예제 #8
0
class _JitterDetector(PVGroup):

    det = pvproperty(value=[0], dtype=float, read_only=True)

    @det.getter
    async def det(self, instance):
        return (await self._read(instance))

    mtr = pvproperty(value=[0], dtype=float)

    exp = pvproperty(value=[1], dtype=float)

    @exp.putter
    async def exp(self, instance, value):
        value = np.clip(value, a_min=0, a_max=None)
        return value
예제 #9
0
class WorkerThreadIOC(PVGroup):
    request = pvproperty(value=0, max_length=1)

    # NOTE the decorator used here:
    @request.startup
    async def request(self, instance, async_lib):
        # This method will be called when the server starts up.
        print('* request method called at server startup')
        self.request_queue = async_lib.ThreadsafeQueue()
        self.Event = async_lib.Event

        # Start a separate thread that consumes requests.
        thread = threading.Thread(
            target=worker,
            daemon=True,
            kwargs=dict(request_queue=self.request_queue))
        thread.start()

    @request.putter
    async def request(self, instance, value):
        print(f'Sending the request {value} to the worker.')
        event = self.Event()
        await self.request_queue.async_put((event, value))
        # The worker calls Event.set() when the work is done.
        await event.wait()
        return value
예제 #10
0
class CustomWrite(PVGroup):
    """
    When a PV is written to, write the new value into a file as a string.
    """
    DIRECTORY = temp_path

    async def my_write(self, instance, value):
        # Compose the filename based on whichever PV this is.
        pv_name = instance.pvspec.attr  # 'A' or 'B', for this IOC
        with open(self.DIRECTORY / pv_name, 'w') as f:
            f.write(str(value))
        print(f'Wrote {value} to {self.DIRECTORY / pv_name}')
        return value

    A = pvproperty(put=my_write, value=[0])
    B = pvproperty(put=my_write, value=[0])
예제 #11
0
class SimpleIOC(PVGroup):
    """
    An IOC with three uncoupled PVs

    Scalar PVs
    ----------
    running (int)
    rbv (int)
    val (int)

    """
    running = pvproperty(value=0)
    rbv = pvproperty(value=0)
    val = pvproperty(value=0)

    @running.startup
    async def running(self, instance, async_lib):
        'Periodically update the value'
        await self.running.write(1)
        self.put_queue = async_lib.ThreadsafeQueue()
        self.get_queue = async_lib.ThreadsafeQueue()
        while True:
            entry = await self.put_queue.async_get()
            pv = entry['pv']
            value = entry['value']
            if pv == 'rbv':
                print("process a 'rbv' put request from a device")
            if pv == 'val':
                print("process a 'val' put request from a device")

    @val.putter
    async def val(self, instance, value):
        """
        called when the a new value is written into "val" PV
        """
        print(
            f"Server: 'rbv' Got 'put' request from outside: new value is {value} and type {type(value)}"
        )
        print('responding to the put request...')

    @val.getter
    async def val(self, instance):
        """
        called when the a new value is readby a client
        """
        print(f"Server: 'rbv' Got 'get' request from outside:")
        print('responding to the get request...')
예제 #12
0
class PointDetector(PVGroup):
    """
    A coupled motor and point detector.  The measurement is
    a noise-free Gaussian centered around 0 with a σ of 5

    exp controls how long the 'exposure' takes

    Readonly PVs
    ------------
    det -> the detector value

    Settable PVs
    ------------
    mtr -> motor position
    exp -> exposure time

    """
    mtr = pvproperty(value=0, dtype=float)

    exp = pvproperty(value=1, dtype=float)

    @exp.putter
    async def exp(self, instance, value):
        value = np.clip(value, a_min=0, a_max=None)
        return value

    det = pvproperty(value=0, dtype=float, read_only=True)

    @det.getter
    async def det(self, instance):
        exposure_time = self.exp.value
        sigma = 5
        center = 0
        c = -1 / (2 * sigma * sigma)
        m = self.mtr.value

        return exposure_time * np.exp((c * (m - center)**2))

    acq = pvproperty(value=0, dtype=float)
    busy = pvproperty(value=0, read_only=True)

    @acq.putter
    async def acq(self, instance, value):
        await self.busy.write(1)
        await asyncio.sleep(self.exp.value)
        await self.busy.write(0)
        return 0
예제 #13
0
class MCAROIGroup(PVGroup):
    label = pvproperty(value='label', name='NM')
    count = pvproperty(value=1, name='', read_only=True)
    net_count = pvproperty(name='N', dtype=unknown, read_only=True)
    preset_count = pvproperty(name='P', dtype=unknown)
    is_preset = pvproperty(name='IP', dtype=unknown)
    bkgnd_chans = pvproperty(name='BG', dtype=unknown)
    hi_chan = pvproperty(name='HI', dtype=unknown)
    lo_chan = pvproperty(name='LO', dtype=unknown)
예제 #14
0
class FakePMPSGroup(PVGroup):
    """
    Fake PV group for simulating incoming PMPS commands.
    """
    t_des = pvproperty(value=0.1,
                       name='T_DES',
                       record='ao',
                       upper_ctrl_limit=1.0,
                       lower_ctrl_limit=0.0,
                       doc='PMPS requested transmission')

    run = pvproperty(value='False',
                     name='RUN',
                     record='bo',
                     enum_strings=['False', 'True'],
                     doc='PMPS Change transmission command',
                     dtype=ChannelType.ENUM)
class IOInterruptIOC(PVGroup):
    t1 = pvproperty(value=2.0)
    image = pvproperty(value=np.random.randint(0,
                                               255,
                                               image_shape,
                                               dtype='uint8').flatten(),
                       dtype=bytes)

    @t1.startup
    async def t1(self, instance, async_lib):
        # Loop and grab items from the queue one at a time
        while True:
            await self.t1.write(time.monotonic())
            await self.image.write(
                np.random.randint(0, 255, image_shape,
                                  dtype='uint8').flatten())
            await async_lib.library.sleep(0.1)
예제 #16
0
class SpeechIOC(PVGroup):
    language = pvproperty(value=['en-US'],
                          doc='Language to use',
                          dtype=caproto.ChannelType.ENUM,
                          enum_strings=list(speech.get_languages()),
                          string_encoding='utf-8')
    speak = pvproperty(value=['text'],
                       doc='Text to speak',
                       string_encoding='utf-8')
    rate = pvproperty(value=[0.5], doc='Normalized speech rate')
    speaking = pvproperty(value=[0])

    @speak.startup
    async def speak(self, instance, async_lib):
        self.voices = AVSpeechSynthesisVoice.speechVoices()
        self.synthesizer = AVSpeechSynthesizer.new()

    @speak.putter
    async def speak(self, instance, value):
        if isinstance(value, (list, tuple)):
            value, = value

        language = self.language.value[0]
        voice = AVSpeechSynthesisVoice.voiceWithLanguage_(language)
        for voice in self.voices:
            if (f'Language: {language}' in str(voice)
                    and 'compact' not in str(voice)):
                print('Chose voice:', voice)
                break

        self.voice = voice
        utterance = AVSpeechUtterance.speechUtteranceWithString_(value)
        rate = self.rate.value[0]

        utterance.rate = rate
        utterance.voice = self.voice
        utterance.useCompactVoice = False

        print(f'Saying {value!r} in {language} at rate {rate}')
        self.synthesizer.speakUtterance_(utterance)

    @speaking.startup
    async def speaking(self, instance, async_lib):
        while True:
            await self.speaking.write(value=[speech.is_speaking()])
            await async_lib.library.sleep(0.1)
예제 #17
0
class UndulatorPV(PVGroup):
    useg_proc = pvproperty(value=0, name=':ConvertK2Gap.PROC')  #Go command
    #gapact = pvproperty(value=0.0, name=':GapAct')
    #gapdes = pvproperty(value=0.0, name=':GapDes')
    kact = pvproperty(value=0.0, name=':KAct', read_only=True)
    kdes = pvproperty(value=0.0, name=':KDes')
    taper_des = pvproperty(value=0.0, name=':TaperDes')
    taper_act = pvproperty(value=0.0, name=':TaperAct', read_only=True)
    symm_act = pvproperty(value=0.0, name=':SymmetryAct', read_only=True)
    serial_n = pvproperty(value=0.0, name=':SerialNum')

    def __init__(self, device_name, element_name, change_callback,
                 initial_values, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.device_name = device_name
        self.element_name = element_name
        self.kact._data['value'] = float(initial_values['kact'])
        self.kdes._data['value'] = float(initial_values['kact'])
        self.taper_des._data['value'] = 0
        self.symm_act._data['value'] = 0
        self.change_callback = change_callback

    @useg_proc.putter
    async def useg_proc(self, instance, value):
        ioc = instance.group
        await asyncio.sleep(0.2)
        await ioc.kact.write(ioc.kdes.value)
        await self.change_callback(self, ioc.kact.value)
예제 #18
0
파일: motor.py 프로젝트: pcdshub/sim-ioc
class TwinCATStatePositioner(PVGroup):
    _delay = 0.2

    state_get = pvproperty(value=0, name='GET_RBV')
    state_set = pvproperty(value=0, name='SET')
    error = pvproperty(value=0.0, name='ERR_RBV')
    error_id = pvproperty(value=0, name='ERRID_RBV')
    error_message = pvproperty(dtype=str, name='ERRMSG_RBV')
    busy = pvproperty(value=0, name='BUSY_RBV')
    done = pvproperty(value=0, name='DONE_RBV')
    reset_cmd = pvproperty_with_rbv(dtype=int, name='RESET')
    config = SubGroup(TwinCATStateConfigAll, prefix='')

    @state_set.startup
    async def state_set(self, instance, async_lib):
        self.async_lib = async_lib
        # Start as "out" and not unknown
        await self.state_get.write(1)

    @state_set.putter
    async def state_set(self, instance, value):
        await self.busy.write(1)
        await self.state_get.write(0)
        await self.async_lib.library.sleep(self._delay)
        await self.state_get.write(value)
        await self.busy.write(0)
        await self.done.write(1)
예제 #19
0
파일: motor.py 프로젝트: pcdshub/sim-ioc
class XpsMotorFields(MotorFields):
    _record_type = 'xps8p'

    stop_pause_go = pvproperty(name='SPG',
                               value='GO',
                               dtype=caproto.ChannelType.ENUM,
                               enum_strings=['STOP', 'PAUSE', 'GO'],
                               doc='PCDS stop-pause-go variant of SPMG',
                               read_only=False)
예제 #20
0
class IOInterruptIOC(PVGroup):
    t1 = pvproperty(value=2.0)
    image = pvproperty(
        value=np.random.randint(0, 256, image_shape, dtype=np.uint8).flatten(),
        dtype=bytes,
    )

    @t1.scan(period=0.1)
    async def t1(self, instance, async_lib):
        # Loop and grab items from the queue one at a time
        await self.t1.write(time.monotonic())

        value = np.random.randint(0, 256, image_shape,
                                  dtype=np.uint8).flatten()
        # caproto will not perform a copy in preprocess_value if you mark
        # the array as read-only by way of flags:
        value.flags.writeable = False
        await self.image.write(value=value)
예제 #21
0
class _JitterDetector(PVGroup):
    det = pvproperty(value=0, dtype=float, read_only=True)

    @det.getter
    async def det(self, instance):
        return (await self._read(instance))

    mtr = pvproperty(value=0, dtype=float, precision=3, record='ai')
    exp = pvproperty(value=1, dtype=float)
    vel = pvproperty(value=1, dtype=float)

    mtr_tick_rate = pvproperty(value=10, dtype=float, units='Hz')

    @exp.putter
    async def exp(self, instance, value):
        value = np.clip(value, a_min=0, a_max=None)
        return value

    @mtr.startup
    async def mtr(self, instance, async_lib):
        instance.ev = async_lib.library.Event()
        instance.async_lib = async_lib

    @mtr.putter
    @no_reentry
    async def mtr(self, instance, value):
        # "tick" at 10Hz
        dwell = 1 / self.mtr_tick_rate.value

        disp = (value - instance.value)
        # compute the total movement time based an velocity
        total_time = abs(disp / self.vel.value)
        # compute how many steps, should come up short as there will
        # be a final write of the return value outside of this call
        N = int(total_time // dwell)

        for j in range(N):
            # hide a possible divide by 0
            step_size = disp / N
            await instance.write(instance.value + step_size)
            await instance.async_lib.library.sleep(dwell)

        return value
예제 #22
0
class CudKlys(PVGroup):
    """
    Represents the PVs used by the Klystron CUD.
    Every PV in here is just a static value, driven by
    the Klystron CUD MATLAB process. 
    """
    onbeam1 = pvproperty(value=0.0, name=':ONBEAM1')
    status = pvproperty(value=0.0, name=':STATUS')
    statusdesc = pvproperty(value='None',
                            name=':STATUS.DESC',
                            dtype=ChannelType.STRING)

    def __init__(self, device_name, element_name, initial_value, *args,
                 **kwargs):
        super().__init__(*args, **kwargs)
        self.device_name = device_name
        self.element_name = element_name
        self.onbeam1._data['value'] = initial_value
        self.status._data['value'] = initial_value
예제 #23
0
class EnumIOC(PVGroup):
    """
    An IOC with some enums.

    Each property here presents itself as a record with the expected fields
    over Channel Access.

    For ``bi`` and ``bo``, the ZNAM and ONAM fields hold the string equivalent
    values for 0 and 1.  These are derived from the ``enum_strings`` keyword
    argument.

    That is, ``bo.ZNAM`` is "Zero Value", ``bo.ONAM`` is ``"One Value"``, such
    that ``caput bo 1`` would show it being set to ``"One Value"``.

    For the mbbi record, the ``ZRST`` (zero string) field, ``ONST`` (one
    string) field, and so on (up to 15), are similarly respected and mapped
    from the ``enum_strings`` keyword argument.

    Scalar PVs
    ----------
    bo (enum) - a binary output (bo) record
    bi (enum) - a binary input (bi) record
    mbbi (enum) - a multi-bit binary input (mbbi) record
    """

    bo = pvproperty(value='One Value',
                    enum_strings=['Zero Value', 'One Value'],
                    record='bo',
                    dtype=ChannelType.ENUM)
    bi = pvproperty(value='a',
                    enum_strings=['a', 'b'],
                    record='bi',
                    dtype=ChannelType.ENUM)
    mbbi = pvproperty(value='one',
                      enum_strings=['zero', 'one', 'two', 'three', 'four'],
                      record='mbbi',
                      dtype=ChannelType.ENUM)

    # A new, easier syntax:
    enum_class = pvproperty(
        value=MyEnum.on,
        record='mbbi',
    )
예제 #24
0
    class Motors(LVGroup):
        names = alsdac.ListMotors()
        for name in names:
            locals()[name] = SubGroup(Motor, prefix=name + '.')

        devices = pvproperty(value=[], dtype=str)

        @devices.getter
        async def devices(self, instance):
            return list(alsdac.ListMotors())
예제 #25
0
    class DigitalInputOutputs(LVGroup):
        names = alsdac.ListDIOs()
        for name in names:
            locals()[name] = SubGroup(DigitalInputOutput, prefix=name + '.')

        devices = pvproperty(value=[], dtype=str)

        @devices.getter
        async def devices(self, instance):
            return list(alsdac.ListDIOs())
예제 #26
0
class SimpleIOC(PVGroup):
    """
    An IOC with three uncoupled read/writable PVs.

    Scalar PVs
    ----------
    A (int)
    B (float)

    Array PVs
    ---------
    C (array of int)
    """
    A = pvproperty(
        value=1,
        doc='An integer',
    )
    B = pvproperty(value=2.0, doc='A float')
    C = pvproperty(value=[1, 2, 3], doc='An array of integers (max length 3)')
예제 #27
0
class HeaterPV(PVGroup):
    power = pvproperty(value=0.0, name=':POWER', precision=1)

    def  __init__(self, change_callback, initial_values, *args, **kwargs):
       super().__init__(*args, **kwargs)
       cav_gradient = initial_values[0] #Necessary? Might only need it for the cryo model
       self.power._data['value'] = initial_values[1]
       
    @power.putter
    async def power(self, instance, value)
예제 #28
0
class ReadingCounter(PVGroup):
    """
    Count the number of times that a PV is read.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tallies = collections.Counter()

    async def my_read(self, instance):
        pv_name = instance.pvspec.attr
        self.tallies.update({pv_name: 1})
        print('tallies:', self.tallies)
        # The act of reading this PV changes its value!
        # Weird but sort of interesting.
        await instance.write(self.tallies[pv_name])
        return instance.value

    A = pvproperty(get=my_read, value=[0])
    B = pvproperty(get=my_read, value=[0])
예제 #29
0
class CameraIOC(PVGroup):
    acquire = pvproperty(value=[0],
                         doc='Process to acquire an image',
                         mock_record='bo')
    shape = pvproperty(value=[image_width, image_height],
                       doc='Image dimensions',
                       read_only=True)
    image = pvproperty(value=[0] * (image_width * image_height),
                       doc='Image data',
                       read_only=True)

    @acquire.putter
    async def acquire(self, instance, value):
        image = photos.capture_image()
        # resize to (width, height)
        image = image.resize((image_width, image_height))
        # and convert to grayscale
        image_array = np.dot(np.asarray(image)[..., :3], [0.299, 0.587, 0.114])
        await self.image.write(image_array.flatten().astype(np.uint32))
예제 #30
0
class StatsPlugin(PVGroup):
    """
    Minimal AreaDetector stats plugin stand-in.

    BTPS will check the array counter update rate and centroid values.
    """
    enable = pvproperty(value=1, name="SimEnable")
    array_counter = pvproperty(value=0, name="ArrayCounter_RBV")
    centroid_x = pvproperty(value=0.0, name="CentroidX_RBV")
    centroid_y = pvproperty(value=0.0, name="CentroidY_RBV")

    @enable.scan(period=1)
    async def enable(self, instance, async_lib):
        if self.enable.value == 0:
            return
       
        await self.array_counter.write(value=self.array_counter.value + 1)
        await self.centroid_x.write(value=random.random())
        await self.centroid_y.write(value=random.random())