Some helpful links:
- EPICS IOC command reference
- EPICS motor record reference
- SPEC motor parameters config_adm reference
- MDrive Serial Programming Reference
Config files with imaginary network addresses can be found in the testdata subdir. Store your real files which where extracted from SPEC in subdir cfg out of the git tree (that subfolder is set to be ignored by GIT). For regenerating the IOC files, run the following (feel free to replace testdata by cfg):
./convert.py generate -c testdata/config_adm.txt -a testdata/addressesAliases.md -l testdata/limits.txt -p ../synapps/support/motor-R7-3-1/modules/motorIms
The -p
argument expects an existing path to the motorIms package source tree structure. The scripts searches for the ims binary and the envPaths file which should exist there after building it successfully. We used the synApps package and our modified assemble script helps with building everything successfully on a Linux Ubuntu system.
Some help is shown as usual by:
./convert.py -h
It creates or updates files in the generated subfolder:
generated/
├── moxa1.cmd
├── moxa1.substitutions
├── moxa2.cmd
├── moxa2.substitutions
├── moxa3.cmd
├── moxa3.substitutions
├── moxa4.cmd
├── moxa4.substitutions
├── moxa5.cmd
├── moxa5.substitutions
├── moxa6.cmd
└── moxa6.substitutions
Start one IOC for the connected motors like this (in this case for old[xy]sam):
./generated/moxa4.cmd
Note: After using SPEC the motors may not be in so called Echo Mode =2 which is a requirement for the EPICS driver to work. The convert script supports a toggle mode to switch the Echo Mode of all motors connected to a certain port (e.g. 5231) at once, by running:
./convert.py toggle -a testdata/addressesAliases.md 5231
Of the motors, the MDRIVE are the real, physical ones as far as I can see and MAC_MOT are aliases. You can ignore all TMCL motors, they are handled by the Trinamic controller (and the spec-config doesn't work for them anyway).
About the meaning of the motors, as far as I know them:
- s1bot, s1top should control the lower and upper blade of slot 1 respectively. Same goes for s1hr and s1hl for the horizontal. The other two slots (2 and 3) have the same motors, only starting with s2/s3.
- The aliases vg1 / hg1 etc. denote the vertical/horizontal opening of the associated slot pairs (s1bot + s1top, etc.) hp/vp stands analogously for horizontal/vertical position (of the slot pair).
- bsr and bsz are the real motors of the beam stop (rotation and vertical translation), aliases are bsh (horizontal) and bsv (vertical)
- detx, dety and detz control the position of the detector. Where detx is the distance between the sample and the detector, dety is the horizontal movement across the beam and detz is the vertical movement.
- dual should be for the movement of the X-ray sources, but I'm not so sure about that.
(See config_adm file for more.)
# Motor cntrl steps sign slew base backl accel nada flags mne name
MOT026 = MDRIVE:3/33 34133 -1 51200 100 -34133 125 0 0x003 oldysam oldysam
MOTPAR:init_sequence = S1=2,0,1;S2=3,0,1
MOTPAR:units = mm
- Field 2, Steps per unit (sign changes direction of motion): 34133
- MRES = UREV/SREV (https://epics-modules.github.io/motor/motorRecord.html#Fields_res)
- MRES = 1/34133 = 0.0000292972
- Field 5, Base rate (Hz): 100 (steps)
- (base rate in steps per sec.) / (steps per unit) = (unit per sec.)
- VBAS = 100 / 34133 = 0.002929716
- (https://epics-modules.github.io/motor/motorRecord.html#Fields_motion)
- Field 4, Steady state rate (Hz): 51200
- (Steady state rate in steps per sec.) / (steps per unit) = (unit per sec.)
- VELO = 51200 / 34133 = 1.5
- (https://epics-modules.github.io/motor/motorRecord.html#Fields_motion)
- Field 7, Acceleration time (msec): 125
- Field 6, Steps for backlash: -34133
- (steps) / (steps per unit) = (distance in unit)
- BDST = -34133 / 34133 = -1
- Note: No extra values for backlash velocity and acceleration are set in SPEC therefore, use the values for forward operation
- Missing in SPEC config: the length of the axis, ie. movement limits set by DHLM and DLLM, what is allowed to be requested by DVAL.
Motor record:
{ P, N, M, DTYP, PORT, DESC, ADDR, EGU, DIR, MRES, VBAS, VELO, ACCL, BDST, BVEL, BACC, DHLM, DLLM, INIT }
{IMS:, "x", "m$(N)", "asynMotor", IMSX, "MDrive23", 0, mm, Pos, 0.0000292972, 0.002929716, 1.5, 0.125, -1, 1.5, 0.125, 100, -100, "S1=2,0,1;S2=3,0,1"}
Motor limits (dial values) and offset can be queried in spec.
for (i = 0; i < MOTORS; i++) {
printf("Motor %s: address %s; lower_limit %g; upper_limit %g; offset %g\n",
motor_name(i), motor_par(i, "device_id"), get_lim(i, -1), get_lim(i, 1), motor_par(i, "offset"));
}
Output is in limits.txt
get_lim(i, -1)
gets the lower limit (dial value) = DLLMget_lim(i, -1)
gets the upper limit (dial value) = DHLMmotor_par(i, "offset")
gets the offset = OFF
Initially stored under motor-R7-3-1/modules/motorIms/iocs/imsIOC/iocBoot/iocIms/maus.cmd
:
#!../../bin/linux-x86_64/ims
#
# make sure the motors are in echo mode 2:
# $ socat - TCP4:192.168.0.5:7434
# XEM=2
# ZEM=2
# <CTRL-J>
#
# In serial comm. (above) print motor Z parameters with:
# Z PR AL
#
# When the ioc is running, check availablePVs with `dbl`, for example:
# epics> dbl
# IMS:mxOffset
# IMS:mxResolution
# IMS:mzOffset
# IMS:mzResolution
# IMS:IOC:1:IMS_S
# IMS:mxDirection
# IMS:mzDirection
# IMS:mx
# IMS:mz
< envPaths
# change to the 'motor-R7-3-1/modules/motorIms/iocs/imsIOC' dir
cd "${TOP}"
# Register all support components
dbLoadDatabase "dbd/ims.dbd"
ims_registerRecordDeviceDriver pdbbase
# Motors substitutions, customize this for your motor
dbLoadTemplate "iocBoot/iocIms/maus.substitutions"
# Configure asyn communication port, first
# drvAsynIPPortConfigure(IOPortName, port, priority, disable auto-connect, no process EOS)
drvAsynIPPortConfigure("moxa4", "192.168.0.5:7434", 0, 0, 0 )
# Configure one controller per motor, each controller just has 1 axis
# motorPortName, portName, deviceName, movingPollPeriod, idlePollPeriod
ImsMDrivePlusCreateController("IMSZ", "moxa4", "Z", 200, 5000)
ImsMDrivePlusCreateController("IMSX", "moxa4", "X", 200, 5000)
# Optional: Enable tracing
#asynSetTraceIOMask("IMS1", 0, 0)
#asynSetTraceMask("IMS1", 0, 9)
# motorUtil (allstop & alldone)
#dbLoadRecords("$(MOTOR)/db/motorUtil.db", "P=ims:")
# Initialize the IOC and start processing records
iocInit()
# motorUtil (allstop & alldone)
#motorUtilInit("ims:")
Initial test settings (records) for motors oldysam and oldzsam stored under iocBoot/iocIms/maus.substitutions
and referenced in the IOCsh script above:
file "$(MOTOR)/db/basic_asyn_motor.db"
{
pattern
{ P, N, M, DTYP, PORT, DESC, ADDR, EGU, DIR, VELO, VBAS, ACCL, BDST, BVEL, BACC, DHLM, DLLM, MRES, PREC, INIT, OFF}
{IMS:, "x", "m$(N)", "asynMotor", IMSX, "MDrive23", 0, mm, Neg, 1.5, 0.002929716, 0.125, -1, 1.5, 0.25, 52, -52, 0.0000292972, 4, "", 0}
{IMS:, "z", "m$(N)", "asynMotor", IMSZ, "MDrive23", 0, mm, Pos, 1.875, 0.002929716, 0.175, 1, 1.5, 0.25, 16, -88, 0.0000585944, 4, "", 0}
}
file "$(TOP)/db/IMS_extra.db"
{
pattern
{DEV, AREA, LOC, PORT, ADDR, TIMEOUT}
{IMS, IOC, 1, IMSX, 0, 1}
{IMS, IOC, 1, IMSZ, 0, 1}
}
Updated values are derived from formulae above, limits as shown in the spec config.
- to run multiple IOCs on a single machine, some kind of gateway is required
- otherwise, on start up, epics shows a warning that another port has to be claimed but it will not be found (and thus used) by the client side