Skip to content
Merged
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
95 changes: 95 additions & 0 deletions apstools/migration/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ID @(#)getinfo.c 6.6 01/15/16 CSS
# Device nodes
SDEV_0 = /dev/ttyUSB0 19200 raw
SDEV_1 = /dev/ttyUSB2 19200 raw
#SDEV_2 = /dev/ttyUSB2 19200 raw
VM_EPICS_M1 = 9idcLAX:m58:c0: 8
VM_EPICS_M1 = 9idcLAX:m58:c1: 8
VM_EPICS_M1 = 9idcLAX:m58:c2: 8
VM_EPICS_M1 = 9idcLAX:mxv:c0: 8
VM_EPICS_M1 = 9idcLAX:pi:c0: 4
VM_EPICS_M1 = 9idcLAX:xps:c0: 8
VM_EPICS_M1 = 9idcLAX:aero:c0: 1
VM_EPICS_M1 = 9idcLAX:mxv:c1: 8
VM_EPICS_M1 = 9idcLAX:aero:c1: 1
VM_EPICS_M1 = 9idcLAX:aero:c2: 1
PSE_MAC_MOT = kohzuE 1
VM_EPICS_M1 = 9ida: 60
VM_EPICS_M1 = 9idcLAX:aero:c3: 1
VM_EPICS_SC = 9idcLAX:vsc:c0 16
# CAMAC Slot Assignments
# CA_name_unit = slot [crate_number]
# Motor cntrl steps sign slew base backl accel nada flags mne name
MOT000 = EPICS_M2:0/1 2000 1 2000 200 50 125 0 0x003 un0 unused0
MOT001 = EPICS_M2:0/2 2000 1 2000 200 50 125 0 0x003 mx mx
MOT002 = EPICS_M2:0/3 2000 1 2000 200 50 125 0 0x003 my my
MOT003 = EPICS_M2:0/4 2000 1 2000 200 50 125 0 0x003 waxsx WAXS X
MOT004 = EPICS_M2:0/5 2000 1 2000 200 50 125 0 0x003 ax ax
MOT005 = EPICS_M2:0/6 2000 1 2000 200 50 125 0 0x003 gslity Gslit_Y
MOT006 = EPICS_M2:0/7 2000 1 2000 200 50 125 0 0x003 az az
MOT007 = EPICS_M2:0/8 2000 1 2000 200 50 125 0 0x003 un7 unused7
MOT008 = EPICS_M2:1/1 2000 1 2000 200 50 125 0 0x003 msx msx
MOT009 = EPICS_M2:1/2 2000 1 2000 200 50 125 0 0x003 msy msy
MOT010 = EPICS_M2:1/3 2000 1 2000 200 50 125 0 0x003 art ART50-100
MOT011 = EPICS_M2:1/4 2000 1 2000 200 50 125 0 0x003 asy asy
MOT012 = EPICS_M2:1/5 2000 1 2000 200 50 125 0 0x003 gslitx Gslit_X
MOT013 = EPICS_M2:1/6 2000 1 2000 200 50 125 0 0x003 tcam tcam
MOT014 = EPICS_M2:1/7 2000 1 2000 200 50 125 0 0x003 camy cam_y
MOT015 = EPICS_M2:1/8 2000 1 2000 200 50 125 0 0x003 tens Tension
MOT016 = EPICS_M2:2/1 2000 1 2000 200 50 125 0 0x003 sx sx
MOT017 = EPICS_M2:2/2 2000 1 2000 200 50 125 0 0x003 sy sy
MOT018 = EPICS_M2:2/3 2000 1 2000 200 50 125 0 0x003 dx dx
MOT019 = EPICS_M2:2/4 2000 1 2000 200 50 125 0 0x003 un19 un19
MOT020 = EPICS_M2:2/5 2000 1 2000 200 50 125 0 0x003 uslvcen uslitvercen
MOT021 = EPICS_M2:2/6 2000 1 2000 200 50 125 0 0x003 uslhcen uslithorcen
MOT022 = EPICS_M2:2/7 2000 1 2000 200 50 125 0 0x003 uslvap uslitverap
MOT023 = EPICS_M2:2/8 2000 1 2000 200 50 125 0 0x003 uslhap uslithorap
MOT024 = EPICS_M2:3/1 2000 1 2000 200 50 125 0 0x003 pin_x pin_x
MOT025 = EPICS_M2:3/2 2000 1 2000 200 50 125 0 0x003 pin_z pin_z
MOT026 = EPICS_M2:3/3 2000 1 2000 200 50 125 0 0x003 gslout GSlit_outb
MOTPAR:read_mode = 7
MOT027 = EPICS_M2:3/4 2000 1 2000 200 50 125 0 0x003 gslinb GSlit_inb
MOTPAR:read_mode = 7
MOT028 = EPICS_M2:3/5 2000 1 2000 200 50 125 0 0x003 gsltop GSlit_top
MOTPAR:read_mode = 7
MOT029 = EPICS_M2:3/6 2000 1 2000 200 50 125 0 0x003 gslbot GSlit_bot
MOTPAR:read_mode = 7
MOT030 = EPICS_M2:3/7 2000 1 2000 200 50 125 0 0x003 un30 unused30
MOT031 = EPICS_M2:3/8 2000 1 2000 200 50 125 0 0x003 pin_y pin_y
MOT032 = EPICS_M2:4/1 2000 1 2000 200 50 125 0 0x003 a2rp USAXS.a2rp
MOT033 = EPICS_M2:4/2 2000 1 2000 200 50 125 0 0x003 m2rp USAXS.m2rp
MOT034 = EPICS_M2:4/3 2000 1 2000 200 50 125 0 0x003 msrp USAXS.msrp
MOT035 = EPICS_M2:4/4 2000 1 2000 200 50 125 0 0x003 asrp USAXS.asrp
MOT036 = EPICS_M2:5/1 2000 1 2000 200 50 125 0 0x003 un36 unused36
MOT037 = EPICS_M2:5/2 2000 1 2000 200 50 125 0 0x003 un37 unused37
MOT038 = EPICS_M2:5/3 2000 1 2000 200 50 125 0 0x003 mst mst
MOT039 = EPICS_M2:5/4 2000 1 2000 200 50 125 0 0x003 ast ast
MOT040 = EPICS_M2:5/5 2000 1 2000 200 50 125 0 0x003 msr msr
MOT041 = EPICS_M2:5/6 2000 1 2000 200 50 125 0 0x003 asr asr
MOT042 = EPICS_M2:5/7 2000 1 2000 200 50 125 0 0x003 un42 unused42
MOT043 = EPICS_M2:5/8 2000 1 2000 200 50 125 0 0x003 un43 unused43
MOT044 = EPICS_M2:6/1 2000 1 2000 200 50 125 0 0x003 ar ar
MOT045 = EPICS_M2:7/1 2000 1 2000 200 50 125 0 0x003 un45 un45
MOT046 = EPICS_M2:7/2 2000 1 2000 200 50 125 0 0x003 un46 un46
MOT047 = EPICS_M2:7/3 2000 1 2000 200 50 125 0 0x003 un47 un47
MOT048 = EPICS_M2:7/4 2000 1 2000 200 50 125 0 0x003 un48 un48
MOT049 = EPICS_M2:7/5 2000 1 2000 200 50 125 0 0x003 un49 un49
MOT050 = EPICS_M2:7/6 2000 1 2000 200 50 125 0 0x003 un50 un50
MOT051 = EPICS_M2:7/7 2000 1 2000 200 50 125 0 0x003 un51 un51
MOT052 = EPICS_M2:7/8 2000 1 2000 200 50 125 0 0x003 un52 un52
MOT053 = EPICS_M2:8/1 2000 1 2000 200 50 125 0 0x003 ay ay
MOT054 = EPICS_M2:9/1 2000 1 2000 200 50 125 0 0x003 dy dy
MOT055 = MAC_MOT:0/0 2000 1 2000 200 50 125 0 0x003 en en
MOTPAR:read_mode = 7
MOT056 = EPICS_M2:10/43 2000 1 2000 200 50 125 0 0x003 InbMS MonoSl_inb
MOT057 = EPICS_M2:10/44 2000 1 2000 200 50 125 0 0x003 OutMS MonoSl_out
MOT058 = EPICS_M2:10/45 2000 1 2000 200 50 125 0 0x003 TopMS MonoSl_top
MOT059 = EPICS_M2:10/46 2000 1 2000 200 50 125 0 0x003 BotMS MonoSl_bot
MOT060 = EPICS_M2:11/1 2000 1 2000 200 50 125 0 0x003 mr mr
# Counter ctrl unit chan scale flags mne name
CNT000 = EPICS_SC 0 0 10000000 0x001 sec seconds
CNT001 = EPICS_SC 0 1 1 0x002 I0 I0
CNT002 = EPICS_SC 0 2 1 0x000 I00 I00
CNT003 = EPICS_SC 0 3 1 0x000 upd2 USAXS_PD
CNT004 = EPICS_SC 0 4 1 0x000 trd TR_diode
CNT005 = EPICS_SC 0 5 1 0x000 I000 I000
228 changes: 228 additions & 0 deletions apstools/migration/spec2ophyd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/env python

"""
read SPEC config file and convert to ophyd setup commands
"""


from collections import OrderedDict
import re


CONFIG_FILE = 'config'
KNOWN_DEVICES = "PSE_MAC_MOT VM_EPICS_M1 VM_EPICS_SC".split()


class SpecDeviceBase(object):
"""
SPEC configuration of a device, such as a multi-channel motor controller
"""

def __init__(self, config_text):
"""parse the line from the SPEC config file"""
# VM_EPICS_M1 = 9idcLAX:m58:c0: 8
nm, args = config_text.split("=")
self.name = nm.strip()
prefix, num_channels = args.split()
self.prefix = prefix
self.index = None
self.num_channels = int(num_channels)

def __str__(self):
fmt = "SpecDeviceBase(index={}, name={}, prefix={}, num_channels={})"
return fmt.format(
self.index,
self.name,
self.prefix,
self.num_channels
)


class ItemNameBase(object):
def item_name_value(self, item):
if hasattr(self, item):
return f"{item}={self.__getattribute__(item)}"


class SpecMotor(ItemNameBase):
"""
SPEC configuration of a motor channel
"""

def __init__(self, config_text):
"""parse the line from the SPEC config file"""
# Motor cntrl steps sign slew base backl accel nada flags mne name
# MOT002 = EPICS_M2:0/3 2000 1 2000 200 50 125 0 0x003 my my
lr = config_text.split(sep="=", maxsplit=1)
self.index = int(lr[0].strip("MOT"))

def pop_word(line, int_result=False):
line = line.strip()
pos = line.find(" ")
l, r = line[:pos].strip(), line[pos:].strip()
if int_result:
l = int(l)
return l, r

self.cntrl, r = pop_word(lr[1])
self.steps, r = pop_word(r, True)
self.sign, r = pop_word(r, True)
self.slew, r = pop_word(r, True)
self.base, r = pop_word(r, True)
self.backl, r = pop_word(r, True)
self.accel, r = pop_word(r, True)
self.nada, r = pop_word(r, True)
self.flags, r = pop_word(r)
self.mne, self.name = pop_word(r)
self.device = None
self.pvname = None

def __str__(self):
items = [self.item_name_value(k) for k in "index mne name".split()]
txt = self.item_name_value("pvname")
if txt is not None:
items.append(txt)
else:
items.append(self.item_name_value("cntrl"))
return "SpecMotor({})".format(", ".join(items))

def setDevice(self, devices):
if self.cntrl.startswith("EPICS_M2"):
device_list = devices.get("VM_EPICS_M1")
if device_list is not None:
uc_str = self.cntrl[len("EPICS_M2:"):]
unit, chan = list(map(int, uc_str.split("/")))
self.device = device_list[unit]
self.pvname = "{}m{}".format(self.device.prefix, chan)


class SpecCounter(ItemNameBase):
"""
SPEC configuration of a counter channel
"""

def __init__(self, config_text):
"""parse the line from the SPEC config file"""
# # Counter ctrl unit chan scale flags mne name
# CNT000 = EPICS_SC 0 0 10000000 0x001 sec seconds

def pop_word(line, int_result=False):
line = line.strip()
pos = line.find(" ")
l, r = line[:pos].strip(), line[pos:].strip()
if int_result:
l = int(l)
return l, r

l, r = pop_word(config_text)
self.index = int(l.strip("CNT"))
l, r = pop_word(r) # ignore "="
self.ctrl, r = pop_word(r)
self.unit, r = pop_word(r, True)
self.chan, r = pop_word(r, True)
self.scale, r = pop_word(r, True)
self.flags, r = pop_word(r)
self.mne, self.name = pop_word(r)
self.device = None
self.pvname = None

def __str__(self):
items = [self.item_name_value(k) for k in "index mne name".split()]
txt = self.item_name_value("pvname")
if txt is not None:
items.append(txt)
else:
items.append(self.item_name_value("ctrl"))
return "SpecCounter({})".format(", ".join(items))

def setDevice(self, devices):
if self.ctrl.startswith("EPICS_SC"):
device_list = devices.get("VM_EPICS_SC")
if device_list is not None:
self.device = device_list[self.unit]
# scalers are goofy, SPEC uses 0-based numbering, scaler uses 1-based
self.pvname = "{}.S{}".format(self.device.prefix, self.chan+1)


class SpecConfig(object):
"""
SPEC configuration
"""

def __init__(self, config_file):
self.config_file = config_file or CONFIG_FILE
self.devices = OrderedDict()
self.motors = OrderedDict()
self.counters = OrderedDict()
self.unhandled = []

def read_config(self, config_file=None):
self.config_file = config_file or self.config_file
with open(self.config_file, 'r') as f:
for line in f.readlines():
line = line.strip()

if line.startswith("#"):
continue

word0 = line.split(sep="=", maxsplit=1)[0].strip()
if word0 in KNOWN_DEVICES:
device = SpecDeviceBase(line)
if device.name not in self.devices:
self.devices[device.name] = []
# 0-based numbering
device.index = len(self.devices[device.name])
self.devices[device.name].append(device)
elif word0 == "MOTPAR:read_mode":
self.unhandled.append(line)
elif re.match("CNT\d*", line) is not None:
counter = SpecCounter(line)
counter.setDevice(self.devices)
self.counters[counter.mne] = counter
elif re.match("MOT\d*", line) is not None:
motor = SpecMotor(line)
motor.setDevice(self.devices)
self.motors[motor.mne] = motor
else:
self.unhandled.append(line)


def create_ophyd_setup(spec_config):
# ophyd configures the counters by device, not by channel
device_list = spec_config.devices.get("VM_EPICS_SC")
if device_list is not None:
import_shown = False
for device in device_list:
mne = "scaler{}".format(device.index)
if not import_shown:
print("from ophyd.scaler import ScalerCH")
import_shown = True
print("{} = {}('{}', name='{}')".format(
mne, "ScalerCH", device.prefix, mne))
chans = []
for counter in spec_config.counters.values():
if counter.device == device:
key = "chan%02d" % (counter.chan+1)
print("# {} : {} ({})".format(key, counter.mne, counter.name))
chans.append(key)
if len(chans) > 0:
print("{}.channels.read_attrs = {}".format(mne, chans))

mne_list = []
for mne, motor in sorted(spec_config.motors.items()):
if motor.pvname is not None:
mne = mne.replace(".", "_")
mne_list.append(mne)
print("{} = {}('{}', name='{}') # {}".format(
mne, "EpicsMotor", motor.pvname, mne, motor.name))



def main():
spec_cfg = SpecConfig(CONFIG_FILE)
spec_cfg.read_config()
create_ophyd_setup(spec_cfg)


if __name__ == "__main__":
main()
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
attrs>=17.4.0
bluesky
databroker[all]
event-model>=1.10
historydict
event-model>=1.8
jsonschema>3
ophyd
pandas
pyRestTable
Expand Down