# Read the position state of the axis: # 0 = not ready # 1 = ready (in working position) # 2 = Moving from working to changer position or in changer position # 3 = Moving from changer to working state record(longin, "$(INSTR)$(M):PositionStateRBV") { field(DTYP, "asynInt32") field(INP, "@asyn($(CONTROLLER),$(AXIS)) POSITION_STATE_RBV") field(SCAN, "I/O Intr") field(PINI, "NO") } # Set to 0 to move the tower into working state and to 1 to move into changer position. # The PV "$(INSTR)$(M):ChangeStateRBV" can be used to check if starting the corresponding # movement was successfull. record(longout, "$(INSTR)$(M):ChangeState") { field(DTYP, "asynInt32") field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) CHANGE_STATE") field(PINI, "NO") field(DRVH, "1") field(DRVL, "0") } # When a value is written to the PV "$(INSTR)$(M):ChangeState", the tower needs to be # idle (PositionState = 0), otherwise the state will not be changed. If the value # of this PV is equal to "$(INSTR)$(M):ChangeState", starting a state change was # successfull, otherwise not. record(longin, "$(INSTR)$(M):ChangeStateRBV") { field(DTYP, "asynInt32") field(INP, "@asyn($(CONTROLLER),$(AXIS)) CHANGE_STATE_RBV") field(SCAN, "I/O Intr") field(PINI, "NO") } # This PV combines the position state and the movement readback value from the # motor record: # - 0, if the tower is in working state or transitioning to change position # - 1, If the tower is in change position or transitioning to working state record(calc, "$(INSTR)$(M):ChangingStateRBV") { field(INPA, "$(INSTR)$(M):PositionStateRBV CP") field(INPB, "$(INSTR)$(M).MOVN CP") field(CALC, "((A == 2 && B == 0) || A == 3) ? 1 : 0") } # Convert the double value from the calc record to an integer value. record(longout, "$(INSTR)$(M):ChangingStateRBV_int") { field(DOL, "$(INSTR)$(M):ChangingStateRBV CP") field(OMSL, "closed_loop") } # Read out the origin of the corresponding axis by the given value. # This PV does nothing for "normal" Turbo PMAC axes. record(ai, "$(INSTR)$(M):Origin") { field(DTYP, "asynFloat64") field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_ORIGIN") field(SCAN, "I/O Intr") field(PINI, "NO") field(VAL, "0") } # Shift the origin of the corresponding axis by the given value. The axis will move to this # position and the hardware controller will internally set this position as the # new "0" value. This PV does nothing for "normal" Turbo PMAC axes. record(ao, "$(INSTR)$(M):AdjustOrigin") { field(DTYP, "Raw Soft Channel") field(PINI, "NO") field(FLNK, "$(INSTR)$(M):ResetAO") field(VAL, "0") } # Reset the PV $(INSTR)$(M):AdjustOrigin to zero after it has been written to and # forward the value to $(INSTR)$(M):WriteAO which does the actual writing. record(seq, "$(INSTR)$(M):ResetAO") { field(DESC, "Write value to hardware then reset to 0") field(DOL1, "$(INSTR)$(M):AdjustOrigin") field(LNK1, "$(INSTR)$(M):WriteAO.VAL PP") # Perform write to hardware field(DOL2, "0.0") field(LNK2, "$(INSTR)$(M):AdjustOrigin.VAL PP") # Reset to zero } # This record forwards the adjustment of the origin to the asyn driver. record(ao, "$(INSTR)$(M):WriteAO") { field(DTYP, "asynFloat64") field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) MOTOR_ADJUST_ORIGIN") field(PINI, "NO") field(VAL, "0") } # This record pair reads the parameter library value for "AdjustOriginHighLimitFromDriver_" # and pushes it to the high limit field of the "$(INSTR)$(M):AdjustOrigin" PV. # This can be used to read limits from the hardware and correspondingly update the motor record from the driver. # The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php. # This record is coupled to the parameter library via AdjustOriginHighLimitFromDriver_ -> MOTOR_AOHL_FROM_DRIVER. record(ai, "$(INSTR)$(M):AOHL_RBV") { field(DTYP, "asynFloat64") field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_AOHL_FROM_DRIVER") field(SCAN, "I/O Intr") field(FLNK, "$(INSTR)$(M):PushAOHL2Field") field(PINI, "NO") } # Abbreviated name because EPICS apparently has a maximum record name length record(ao, "$(INSTR)$(M):PushAOHL2Field") { field(DOL, "$(INSTR)$(M):AOHL_RBV NPP") field(OUT, "$(INSTR)$(M):AdjustOrigin.DRVH") field(OMSL, "closed_loop") field(PINI, "NO") } # This record pair reads the parameter library value for "motorLowLimitFromDriver_" # and pushes it to the low limit field of the "$(INSTR)$(M):AdjustOrigin" PV. # This can be used to read limits from the hardware and correspondingly update the motor record from the driver. # The implementation strategy is taken from https://epics.anl.gov/tech-talk/2022/msg00464.php. # This record is coupled to the parameter library via AdjustOriginLowLimitFromDriver_ -> MOTOR_AOLL_FROM_DRIVER. record(ai, "$(INSTR)$(M):AOLL_RBV") { field(DTYP, "asynFloat64") field(INP, "@asyn($(CONTROLLER),$(AXIS)) MOTOR_AOLL_FROM_DRIVER") field(SCAN, "I/O Intr") field(FLNK, "$(INSTR)$(M):PushAOLL2Field") field(PINI, "NO") } # Abbreviated name because EPICS apparently has a maximum record name length record(ao, "$(INSTR)$(M):PushAOLL2Field") { field(DOL, "$(INSTR)$(M):AOLL_RBV NPP") field(OUT, "$(INSTR)$(M):AdjustOrigin.DRVL") field(OMSL, "closed_loop") field(PINI, "NO") }