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

LakeshoreIOC #67

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ env:
- secure: "FhNkkbod0Wc/zUf9cTvwziAYHcjfte2POf+hoVSmC+v/RcYKCNCo+mGGMhF9F4KyC2nzvulfzow7YXoswZqav4+TEEu+mpuPaGlf9aqp8V61eij8MVTwonzQEYmHAy3KatwXxyvvhQpfj3gOuDVolfOg2MtNZi6QERES4E1sjOn714fx2HkVxqH2Y8/PF/FzzGeJaRlVaVci0EdIJ5Ss5c5SjO6JGgxj4hzhTPHjTaLjdLHlVhuB9Yatl80zbhGriljLcDQTHmoSODwBpAh5YLDUZq6B9vomaNB9Hb3e0D5gItjOdj53v6AsHU8LkncZMvsgJgh2sZZqMO6nkpHcYPwJgbPbKd3RtVlk6Kg/tvKQk0rMcxl5fFFeD2i9POnANg/xJsKN6yAEY3kaRwQtajQmlcicSa/wdwv9NhUTtBmA/mnyzxHbQXrB0bEc2P2QVu7U8en6dWaOAqc1VCMrWIhp2ADNWb7JZhYj70TgmExIU3UH8qlMb6dyx50SJUE9waJj3fiiZVkjh+E568ZRSMvL9n+bLlFt4uDT4AysSby6cj+zjfNViKFstTAqjyd5VJEvCoUu73vNzWEiWFtEvKKVL1P3pbLN/G3aSSJMa5fc1o+2lRUwdwNNOOdH6iKBDZGNpE8nGDlTP2b2dhFyEt8nICKJhbgU208jhyyH8Vk="

script:
- export OPHYD_CONTROL_LAYER=caproto
- coverage run -m pytest # Run the tests and check for test coverage.
- coverage report -m # Generate test coverage report.
- codecov # Upload the report to codecov.
Expand Down
240 changes: 240 additions & 0 deletions nslsii/iocs/lakeshore_control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
#!/usr/bin/env python3
from caproto.server import pvproperty, PVGroup
from caproto import ChannelType


class ControlRecord(PVGroup):

def __init__(self, prefix, *, indx, ioc, **kwargs):
super().__init__(prefix, **kwargs)
self._indx = indx
self.ioc = ioc

_false_true_states = ['False', 'True']

# PVPositioner required attributes

_rb_val = 0.

setpoint = pvproperty(value=_rb_val,
dtype=ChannelType.DOUBLE,
name='}}T-SP')
readback = pvproperty(value=_rb_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}T-RB')

_done_val = 0.

done = pvproperty(value='False',
read_only=False,
enum_strings=_false_true_states,
dtype=ChannelType.ENUM,
name='}}Sts:Ramp-Sts')

# top level attributes

_heater_range_val = 0.
_heater_status_val = 0.

heater_range = pvproperty(value=_heater_range_val,
dtype=ChannelType.DOUBLE,
name='}}Val:Range-Sel')
heater_status = pvproperty(value=_heater_status_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Err:Htr-Sts')

_mode_val = 0.
_enable_val = 0.
_target_channel_val = ''

mode = pvproperty(value=_mode_val,
dtype=ChannelType.DOUBLE,
name='}}Mode-Sel')
enable = pvproperty(value=_enable_val,
dtype=ChannelType.DOUBLE,
name='}}Enbl-Sel')
target_channel = pvproperty(value=_target_channel_val,
dtype=ChannelType.STRING,
name='}}Out-Sel')

# ramp attributes

_ramp_enable_val = 0.
_ramp_rate_val = 5. # degree/s

ramp_enable = pvproperty(value=_ramp_enable_val,
dtype=ChannelType.DOUBLE,
name='}}Enbl:Ramp-Sel')

ramp_rate_rb = pvproperty(value=_ramp_rate_val,
dtype=ChannelType.DOUBLE,
name='}}Val:Ramp-RB')
ramp_rate_sp = pvproperty(value=_ramp_rate_val,
dtype=ChannelType.DOUBLE,
name='}}Val:Ramp-SP')

# PID loop parameters

_pid_proportional_val = 0.
_pid_integral_val = 0.
_pid_derivative_val = 0.

pid_proportional_rb = pvproperty(value=_pid_proportional_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Gain:P-RB')
pid_proportional_sp = pvproperty(value=_pid_proportional_val,
dtype=ChannelType.DOUBLE,
name='}}Gain:P-SP')

pid_integral_rb = pvproperty(value=_pid_integral_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Gain:I-RB')
pid_integral_sp = pvproperty(value=_pid_integral_val,
dtype=ChannelType.DOUBLE,
name='}}Gain:I-SP')

pid_derivative_rb = pvproperty(value=_pid_derivative_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Gain:D-RB')
pid_derivative_sp = pvproperty(value=_pid_derivative_val,
dtype=ChannelType.DOUBLE,
name='}}Gain:D-SP')

# output parameters

_out_current_val = 0.
_out_man_current_val = 0.
_out_max_current_val = 0.
_out_resistance_val = 0.

out_current = pvproperty(value=_out_current_val,
dtype=ChannelType.DOUBLE,
name='}}Out-I')

out_man_current_rb = pvproperty(value=_out_man_current_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Out:Man-RB')
out_man_current_sp = pvproperty(value=_out_man_current_val,
dtype=ChannelType.DOUBLE,
name='}}Out:Man-SP')

out_max_current_rb = pvproperty(value=_out_max_current_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Out:MaxI-RB')
out_max_current_sp = pvproperty(value=_out_max_current_val,
dtype=ChannelType.DOUBLE,
name='}}Out:MaxI-SP')

out_resistance_rb = pvproperty(value=_out_resistance_val,
read_only=True,
dtype=ChannelType.DOUBLE,
name='}}Out:R-RB')
out_resistance_sp = pvproperty(value=_out_resistance_val,
dtype=ChannelType.DOUBLE,
name='}}Out:R-SP')

# Putter/Getter Methods

@setpoint.putter
async def setpoint(self, instance, value):

# select channel
prefix = self.ioc.prefix.replace('{', '{'*2)
channel = self._target_channel_val
t_k = f'{prefix}-Chan:{channel}'
if t_k in self.ioc.groups:
pass
else:
return instance.value
t_v = self.ioc.groups[t_k]

# apply cmd
indx = self._indx
cmd = f'{value},{indx}'
await t_v.cmd.write(value=cmd)

self._rb_val = value
return value

@done.getter
async def done(self, instance):
return self._done_val

@target_channel.getter
async def target_channel(self, instance):
return self._target_channel_val

@target_channel.putter
async def target_channel(self, instance, value):
self._target_channel_val = value
malitsky marked this conversation as resolved.
Show resolved Hide resolved
return value

@ramp_rate_rb.getter
async def ramp_rate_rb(self, instance):
return self._ramp_rate_val

@ramp_rate_sp.putter
async def ramp_rate_sp(self, instance, value):
self._ramp_rate_val = value
return value

@pid_proportional_rb.getter
async def pid_proportional_rb(self, instance):
return self._pid_proportional_val

@pid_proportional_sp.putter
async def pid_proportional_sp(self, instance, value):
self._pid_proportional_val = value
return value

@pid_integral_rb.getter
async def pid_integral_rb(self, instance):
return self._pid_integral_val

@pid_integral_sp.putter
async def pid_integral_sp(self, instance, value):
self._pid_integral_val = value
return value

@pid_derivative_rb.getter
async def pid_derivative_rb(self, instance):
return self._pid_derivative_val

@pid_derivative_sp.putter
async def pid_derivative_sp(self, instance, value):
self._pid_derivative_val = value
return value

@out_man_current_rb.getter
async def out_man_current_rb(self, instance):
return self._out_man_current_val

@out_man_current_sp.putter
async def out_man_current_sp(self, instance, value):
self._out_man_current_val = value
return value

@out_max_current_rb.getter
async def out_max_current_rb(self, instance):
return self._out_max_current_val

@out_max_current_sp.putter
async def out_max_current_sp(self, instance, value):
self._out_max_current_val = value
return value

@out_resistance_rb.getter
async def out_resistance_rb(self, instance):
return self._out_resistance_val

@out_resistance_sp.putter
async def out_resistance_sp(self, instance, value):
self._out_resistance_val = value
return value
53 changes: 53 additions & 0 deletions nslsii/iocs/lakeshore_ioc_sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
from caproto.server import PVGroup, ioc_arg_parser, run

from nslsii.iocs.lakeshore_temperature import TemperatureRecord
from nslsii.iocs.lakeshore_control import ControlRecord


class LakeshoreIOC(PVGroup):
"""
Simulates a Lakeshore IOC.
"""

def __init__(self, prefix, *, groups, **kwargs):
super().__init__(prefix, **kwargs)
self.groups = groups


def create_ioc(prefix, temperatures, controls, **ioc_options):

groups = {}

ioc = LakeshoreIOC(prefix, groups=groups, **ioc_options)

for t in temperatures:
t_prefix = f'{prefix}-Chan:{t}'
print('t_prefix:', t_prefix)
groups[t_prefix] = TemperatureRecord(t_prefix, indx=t, ioc=ioc)

for c in controls:
c_prefix = f'{prefix}-Out:{c}'
print('c_prefix:', c_prefix)
groups[c_prefix] = ControlRecord(c_prefix, indx=c, ioc=ioc)

for prefix, group in groups.items():
ioc.pvdb.update(**group.pvdb)

return ioc


if __name__ == '__main__':

ioc_options, run_options = ioc_arg_parser(
default_prefix='test:{{{{',
desc='Lakeshore IOC.')

temperatures = ['A', 'B', 'C', 'D']
controls = [1, 2]

ioc = create_ioc(temperatures=temperatures,
controls=controls,
**ioc_options)

run(ioc.pvdb, **run_options)
Loading