Skip to content

Commit

Permalink
Add tune feedback functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
T-Nicholls committed May 7, 2019
1 parent e7dcc1e commit 9f61e1b
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 193 deletions.
3 changes: 2 additions & 1 deletion ioc/atip_ioc_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
# Create PVs.
server = atip_server.ATIPServer(ring_mode, os.path.join(here, 'limits.csv'),
os.path.join(here, 'feedback.csv'),
os.path.join(here, 'mirrored.csv'))
os.path.join(here, 'mirrored.csv'),
os.path.join(here, 'tunefb.csv'))

# Add special case out record for SOFB to write to.
builder.SetDeviceName('CS-CS-MSTAT-01')
Expand Down
107 changes: 74 additions & 33 deletions ioc/atip_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import atip
import numpy
import pytac
from cothread.catools import camonitor
from cothread.catools import camonitor, caget
from pytac.device import BasicDevice
from pytac.exceptions import HandleException, FieldException
from softioc import builder

from solution import camonitor_mask, caput_mask, summate, collate, transform
from solution import callback_set, callback_refresh, caget_mask, caput_mask
from solution import summate, collate, transform


class ATIPServer(object):
Expand All @@ -36,7 +37,7 @@ class ATIPServer(object):
readback only.
"""
def __init__(self, ring_mode, limits_csv=None, feedback_csv=None,
mirror_csv=None):
mirror_csv=None, tune_csv=None):
"""
Args:
ring_mode (string): The ring mode to create the lattice in.
Expand All @@ -48,27 +49,32 @@ def __init__(self, ring_mode, limits_csv=None, feedback_csv=None,
information see create_csv.py.
"""
self.lattice = atip.utils.loader(ring_mode, self.update_pvs)
self.tune_feedback_status = False
self._pv_monitoring = False
self._tune_fb_csv_path = tune_csv
self._in_records = {}
self._out_records = {}
self._rb_only_records = []
self._feedback_records = {}
self._mirrored_records = {}
self._monitored_pvs = {}
self._offset_pvs = {}
print("Starting record creation.")
self._create_records(limits_csv)
if feedback_csv is not None:
self._create_feedback_records(feedback_csv)
self._all_in = {record.name: record for record in
self._in_records.keys() +
self._feedback_records.values()}

if mirror_csv is not None:
self._create_mirror_records(mirror_csv)
print("Finished creating all {0} records.".format(self.total_records))
print("Finished creating all {0} records."
.format(len(self._all_record_names) +
len(self._mirrored_records)))

@property
def total_records(self):
return sum([len(self._in_records), len(self._out_records),
len(self._feedback_records)])
def _all_record_names(self):
return {record.name: record for record in self._in_records.keys() +
self._out_records.keys() + self._feedback_records.values()}


def update_pvs(self):
"""The callback function passed to ATSimulator during lattice creation,
Expand All @@ -83,8 +89,9 @@ def update_pvs(self):
data_source=pytac.SIM)
rb_record.set(value)
else:
value = self.lattice[index-1].get_value(field, units=pytac.ENG,
data_source=pytac.SIM)
value = self.lattice[index - 1].get_value(
field, units=pytac.ENG, data_source=pytac.SIM
)
rb_record.set(value)

def _create_records(self, limits_csv):
Expand Down Expand Up @@ -137,10 +144,11 @@ def on_update(value, name=set_pv): # noqa E306
DRVL=lower, DRVH=upper,
PREC=precision,
initial_value=value,
on_update=on_update)
on_update=on_update,
always_update=True)
# how to solve the index problem?
self._in_records[in_record] = (element.index, 'b0')
self._out_records[out_record.name] = in_record
self._out_records[out_record] = in_record
bend_set = True
else:
# Create records for all other families.
Expand Down Expand Up @@ -173,8 +181,9 @@ def on_update(value, name=set_pv):
DRVL=lower, DRVH=upper,
PREC=precision,
initial_value=value,
on_update=on_update)
self._out_records[out_record.name] = in_record
on_update=on_update,
always_update=True)
self._out_records[out_record] = in_record
# Now for lattice fields.
lat_fields = self.lattice.get_fields()
for field in set(lat_fields[pytac.LIVE]) & set(lat_fields[pytac.SIM]):
Expand All @@ -200,11 +209,16 @@ def _on_update(self, name, value):
name (str): The name of record object that has just been set to.
value (number): The value that has just been set to the record.
"""
in_record = self._out_records[name]
index, field = self._in_records[in_record]
self.lattice[index-1].set_value(field, value, units=pytac.ENG,
data_source=pytac.SIM)
in_record = self._out_records[self._all_record_names[name]]
in_record.set(value)
index, field = self._in_records[in_record]

if self.tune_feedback_status is True:
offset_pv = self._offset_pvs[name]
offset = caget(offset_pv)
value = value + offset
self.lattice[index - 1].set_value(field, value, units=pytac.ENG,
data_source=pytac.SIM)

def _create_feedback_records(self, feedback_csv):
"""Create all the feedback records from the .csv file at the location
Expand Down Expand Up @@ -255,12 +269,17 @@ def _create_mirror_records(self, mirror_csv):
monitor = input_pvs
else:
monitor = line['monitor'].split(', ')
# Convert input pvs to record objects
input_records = []
for pv in input_pvs:
try:
input_records.append(self._all_record_names[pv])
except KeyError:
input_records.append(caget_mask(pv))
# Create output record.
prefix, suffix = line['out'].split(':', 1)
builder.SetDeviceName(prefix)
if line['record type'] == '':
output_record = self._all_in[line['out']]
elif line['record type'] == 'caput':
if line['record type'] == 'caput':
output_record = caput_mask(line['out'])
elif line['record type'] == 'aIn':
value = float(line['value'])
Expand All @@ -273,8 +292,8 @@ def _create_mirror_records(self, mirror_csv):
dtype=float)
output_record = builder.Waveform(suffix, initial_value=value)
else:
raise TypeError("Record type {0} doesn't currently support "
"mirroring, please enter 'aIn', 'longIn', or "
raise TypeError("{0} isn't a supported mirroring output type;"
"please enter 'caput', 'aIn', 'longIn', or "
"'Waveform'.".format(line['record type']))
# Update the mirror dictionary.
for pv in monitor:
Expand All @@ -287,11 +306,11 @@ def _create_mirror_records(self, mirror_csv):
transformation = transform(numpy.invert, output_record)
self._mirrored_records[monitor[0]].append(transformation)
elif line['mirror type'] == 'summate':
summation_object = summate(input_pvs, output_record)
summation_object = summate(input_records, output_record)
for pv in monitor:
self._mirrored_records[pv].append(summation_object)
elif line['mirror type'] == 'collate':
collation_object = collate(input_pvs, output_record)
collation_object = collate(input_records, output_record)
for pv in monitor:
self._mirrored_records[pv].append(collation_object)
else:
Expand All @@ -300,22 +319,44 @@ def _create_mirror_records(self, mirror_csv):
" 'inverse'.".format(line['mirror type']))

def monitor_mirrored_pvs(self):
self._pv_monitoring = True
for pv, output in self._mirrored_records.items():
mask = camonitor_mask(output)
mask = callback_set(output)
try:
self._monitored_pvs[pv] = camonitor(pv, mask.callback)
except ImportError as e:
except Exception as e:
warn(e)

def refresh_pv(self, pv_name):
def refresh_record(self, pv_name):
try:
record = self._all_in[pv_name]
record = self._all_record_names[pv_name]
except KeyError:
raise ValueError("{0} is not an in record or was not created by "
"this server.".format(pv_name))
raise ValueError("{0} is not the name of a record created by this "
"server.".format(pv_name))
else:
record.set(record.get())

def start_tune_feedback(self, tune_csv=None):
if tune_csv is not None:
self._tune_fb_csv_path = tune_csv
if self.tune_fb_csv_path is None:
raise ValueError("No tune feedback csv file was given at start-up,"
" please provide one now; i.e. server.start_tune_"
"feedback(path_to_csv)")
csv_reader = csv.DictReader(open(tune_csv))
if not self._pv_monitoring:
self.monitor_mirrored_pvs()
for line in csv_reader:
quad_pv = line['quad_set_pv']
offset_pv = line['offset_pv']
self._offset_pvs[quad_pv] = offset_pv
mask = callback_refresh(self, quad_pv)
try:
self._monitored_pvs[offset_pv] = camonitor(offset_pv,
mask.callback)
except Exception as e:
warn(e)

def set_feedback_record(self, index, field, value):
"""Set a value to the feedback in records.
Expand Down
17 changes: 14 additions & 3 deletions ioc/create_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ def generate_mirrored_pvs():
bpm_y_pvs = lattice.get_element_pv_names('BPM', 'y', pytac.RB)
data.append(('', ', '.join(bpm_y_pvs), 'SR-DI-EBPM-01:SA:Y',
[0] * len(bpm_y_pvs), 'Waveform', 'collate'))
return data


def generate_tune_pvs():
lattice = atip.utils.loader()
data = [('quad_set_pv', 'offset_pv')]
# Offset PV for quadrupoles in tune feedback.
tune_pvs = []
offset_pvs = []
Expand All @@ -147,8 +153,7 @@ def generate_mirrored_pvs():
offset_pvs.append('SR-CS-TFB-01:{0}{1}{2}:I'.format(pv[2:4], pv[9:12],
pv[13:15]))
for offset_pv, tune_pv in zip(offset_pvs, tune_pvs):
data.append((offset_pv, ', '.join([offset_pv, tune_pv]), tune_pv, 0.0,
'caput', 'summate'))
data.append((tune_pv, offset_pv))
return data


Expand Down Expand Up @@ -182,15 +187,21 @@ def parse_arguments():
help="Filename for output pv limits CSV file",
default="mirrored.csv",
)
parser.add_argument(
"--tune",
help="Filename for output pv limits CSV file",
default="tunefb.csv",
)
return parser.parse_args()


if __name__ == "__main__":

args = parse_arguments()
data = generate_feedback_pvs()
write_data_to_file(data, args.feedback)
data = generate_pv_limits()
write_data_to_file(data, args.limits)
data = generate_mirrored_pvs()
write_data_to_file(data, args.mirrored)
data = generate_tune_pvs()
write_data_to_file(data, args.tune)

0 comments on commit 9f61e1b

Please sign in to comment.