Source code for py4syn.epics.PseudoMotorClass

"""Pseudo Class

Python class using basic a set of Epics Motor and formulas to create a Pseudo Motor.

:platform: Unix
:synopsis: Python Class for Pseudo Motors control

.. moduleauthor:: Hugo Slepicka <hugo.slepicka@lnls.br>
.. moduleauthor:: Henrique Ferreira Canova <henrique.canova@lnls.br>

"""

from epics import ca
from math import *
import numpy
from py4syn import *
from py4syn.epics.IScannable import IScannable
from py4syn.epics.StandardDevice import StandardDevice
from py4syn.epics import MotorClass
from py4syn.epics.MotorClass import Motor

[docs]class motorTarget(): """ Class to globally control virtual Motor targets """
[docs] def __init__(self): """ **Constructor** Parameters ---------- None """ self.targets = {}
[docs] def __getitem__(self, m): """ Get current value of the virtual Motor positioning Parameters ---------- m : `dictionary` Represents the device target, virtual Motor, in the `mtrDB` global array of devices Returns ------- `double` or `dictionary` item Read the current Positioning value of virtual Motor (Real) .. note:: If `m` parameter is received and it is in the targets list, then its correspondent target is returned (as a `dictionary` item), otherwise, real position of virtual Motor is returned (as a `double` value) """ global mtrDB if m in self.targets: return self.targets[m] return mtrDB[m].getRealPosition()
[docs] def __setitem__(self, m, pos): """ Set a position value to the virtual Motor target received as input parameter Parameters ---------- m : `dictionary` Represents the target device, virtual Motor, in the `mtrDB` global array of devices pos : `double` The desired position to set """ self.targets[m] = pos
[docs] def keys(self): """ Get keys (indexes) of all virtual Motor targets Returns ------- `integer array` Read the keys (indexes) from all targets """ return self.targets.keys()
[docs]class motorPosition(): """ Class to globally control positioning of virtual Motor """
[docs] def __getitem__(self, m): """ Get current value of the virtual Motor positionin Parameters ---------- m : `dictionary` Represents the device, virtual Motor, in the `mtrDB` global array of devices Returns ------- `double` Read the current value (virtual Motor Real Position) """ global mtrDB return mtrDB[m].getRealPosition()
[docs]class motorTargetDial(): """ Class to globally control virtual Motor target using Dial fields """
[docs] def __init__(self): """ **Constructor** Parameters ---------- None """ self.targets = {}
[docs] def __getitem__(self, m): """ Get current value of the virtual Motor positioning using Dial field Parameters ---------- m : `dictionary` Represents the device target, virtual Motor, in the `mtrDB` global array of devices Returns ------- `double` or `dictionary` item Read the current Positioning value of virtual Motor (Real) .. note:: If `m` parameter is received and it is in the targets list, then its correspondent target is returned (as a `dictionary` item), otherwise, real position of virtual Motor, using the Dial field, is returned (as a `double` value) """ global mtrDB if m in self.targets: return self.targets[m] return mtrDB[m].getDialRealPosition()
[docs] def __setitem__(self, m, pos): """ Set a position value to the virtual Motor target received as input parameter Parameters ---------- m : `dictionary` Represents the target device, virtual Motor, in the `mtrDB` global array of devices pos : `double` The desired position to set """ self.targets[m] = pos
[docs] def keys(self): """ Get keys (indexes) of all virtual Motor targets Returns ------- `integer array` Read the keys (indexes) from all targets """ return self.targets.keys()
[docs]class motorPositionDial(): """ Class to globally control positioning of virtual Motor using Dial fields """
[docs] def __getitem__(self, m): """ Get current value of the virtual Motor positioning using Dial field Parameters ---------- m : `dictionary` Represents the device, virtual Motor, in the `mtrDB` global array of devices Returns ------- `double` Read the current Positioning value of virtual Motor (Real) using Dial field """ global mtrDB return mtrDB[m].getDialRealPosition()
class motorTargetRaw(): """ Class to globally control virtual Motor target using Raw fields """ def __init__(self): """ **Constructor** Parameters ---------- None """ self.targets = {} def __getitem__(self, m): """ Get current value of the virtual Motor positioning using Raw field Parameters ---------- m : `dictionary` Represents the device target, virtual Motor, in the `mtrDB` global array of devices Returns ------- `double` or `dictionary` item Read the current Positioning value of virtual Motor (Real) .. note:: If `m` parameter is received and it is in the targets list, then its correspondent target is returned (as a `dictionary` item), otherwise, real position of virtual Motor, using the Raw field, is returned (as a `double` value) """ global mtrDB if m in self.targets: return self.targets[m] return mtrDB[m].getRawRealPosition() def __setitem__(self, m, pos): """ Set a position value to the virtual Motor target received as input parameter Parameters ---------- m : `dictionary` Represents the target device, virtual Motor, in the `mtrDB` global array of devices pos : `double` The desired position to set """ self.targets[m] = pos def keys(self): """ Get keys (indexes) of all virtual Motor targets Returns ------- `integer array` Read the keys (indexes) from all targets """ return self.targets.keys() class motorPositionRaw(): """ Class to globally control positioning of virtual Motor using Raw fields """ def __getitem__(self, m): """ Get current value of the virtual Motor positioning using Raw field Parameters ---------- m : `dictionary` Represents the device, virtual Motor, in the `mtrDB` global array of devices Returns ------- `double` Read the current Positioning value of virtual Motor (Real) using Raw field """ global mtrDB return mtrDB[m].getRawRealPosition()
[docs]class PseudoMotor(IScannable, StandardDevice): """ Class to control Pseudo-Motor (virtual Motor). Examples -------- >>> from py4syn.epics.PseudoMotorClass import PseudoMotor >>> >>> def createPseudoMotor(mnemonic="", description="", backwardFormula="", forwardFormulasDict= []): ... ... new_pseudo_motor = '' ... ... try: ... new_motor = PseudoMotor("motorName", "pseudo-motor to help controlling experiment", "", []) ... print "Motor " + pvName + " created with success!" ... except Exception,e: ... print "Error: ",e ... ... return new_pseudo_motor """ def __init__(self, mnemonic, description, backwardFormula, forwardFormulasDict): """ **Pseudo Motor class Constructor** Parameters ---------- mnemonic : `string` Motor mnemonic description : `string` Motor Description backwardFormula : `string` Mathematical Formula used to calculate the Pseudo motor position based on other motors forwardFormulasDict : `dictionary` Dictionary containing mathematical relations to move each of the motors involved in the pseudo motor movement """ StandardDevice.__init__(self, mnemonic) self.name = mnemonic self.description = description self.backFormula = backwardFormula self.forwardDict = forwardFormulasDict def __str__(self): return self.getMnemonic()
[docs] def getDirection(self): """ Read the Pseudo motor direction Returns ------- `integer` .. note:: 0. Positive direction; 1. Negative direction. """ pass
[docs] def isMoving(self): """ Check if any of the motors are moving Returns ------- `boolean` .. note:: - **True** -- At least one Motor is being moved; - **False** -- **NO** one of the Motors is being moved. """ global mtrDB aux = False for m in self.forwardDict: if not mtrDB[m].isMoving() and (mtrDB[m].isAtHighLimitSwitch() or mtrDB[m].isAtLowLimitSwitch()): self.stop() return False if mtrDB[m].isMoving(): aux = True return aux
[docs] def isAtLowLimitSwitch(self): """ Check if the low limit switch of any of the motors is activated Returns ------- `int` .. note:: - **1** -- At least one Motor is at Low Limit; - **0** -- **NO** one of the Motors is at Low Limit. """ global mtrDB for m in self.forwardDict: if mtrDB[m].isAtLowLimitSwitch(): return 1 return 0
[docs] def isAtHighLimitSwitch(self): """ Check if the high limit switch of any of the motors is activated Returns ------- `int` .. note:: - **1** -- At least one Motor is at High Limit; - **0** -- **NO** one of the Motors is at High Limit. """ global mtrDB for m in self.forwardDict: if mtrDB[m].isAtHighLimitSwitch(): return 1 return 0
[docs] def getDescription(self): """ Read the motor description based on the `DESC` (Description) field of virtual Motor Returns ------- `string` """ return self.description
[docs] def getLimitValue(self, high): """ Auxiliary method called by :meth:`getHighLimitValue` and :meth:`getLowLimitValue` to get the high and low limit values of a pseudo motor. The limits are calculated based on a maximization or minimization, depending on the `high` parameter, of the pseudo motor equation, considering the bounds of the real motors. See :meth:`getHighLimitValue`, :meth:`getLowLimitValue` Parameters ---------- high : `bool` True if the high limit value is requested, false if the low limit is requested. Returns ------- `double` """ from scipy.optimize import minimize # Optimization function minimizes, so invert when requesting the high limit sign = -1 if high else 1 def getPosition(args): env = {motor: i for (i, motor) in enumerate(self.forwardDict)} env['A'] = args return sign*eval(self.backFormula, env) bounds = [] x0 = [] for motor in self.forwardDict: low = mtrDB[motor].getLowLimitValue() high = mtrDB[motor].getHighLimitValue() v = mtrDB[motor].getValue() bounds.append((low, high)) x0.append(v) return sign*minimize(getPosition, x0, bounds=bounds).fun
[docs] def getHighLimitValue(self): """ Read the motor high limit based on the `HLM` (User High Limit) field of virtual Motor Returns ------- `double` """ return self.getLimitValue(high=True)
[docs] def getLowLimitValue(self): """ Read the motor low limit based on the `LLM` (User Low Limit) field of virtual Motor Returns ------- `double` """ return self.getLimitValue(high=False)
[docs] def getDialHighLimitValue(self): """ Read the motor dial high limit based on the `DHLM` (Dial High Limit) field of virtual Motor Returns ------- `double` """ return numpy.nan
[docs] def getDialLowLimitValue(self): """ Read the motor dial low limit based on the `DLLM` (Dial Low Limit) field of virtual Motor Returns ------- `double` """ return numpy.nan
[docs] def getBacklashDistanceValue(self): """ Read the motor backlash distance based on the `BDST` (Backlash Distance, `EGU`) field of virtual Motor Returns ------- `double` """ return numpy.nan
[docs] def getVariableOffset(self): """ Read the motor variable offset based on the `VOF` (Variable Offset) field of virtual Motor Returns ------- `integer` """ return numpy.nan
[docs] def getFreezeOffset(self): """ Read the motor freeze offset based on the `FOF` (Freeze Offset) field of virtual Motor Returns ------- `integer` """ return numpy.nan
[docs] def getOffset(self): """ Read the motor offset based on the `OFF` (User Offset, `EGU`) field of virtual Motor Returns ------- `string` """ return numpy.nan
[docs] def getRealPosition(self): """ Read the motor real position based on the `RBV` (User Readback Value) field of virtual Motor Returns ------- `double` """ global mtrDB global A global T exec(self.__defineMotors()) return eval(self.backFormula)
[docs] def getRawPosition(self): """ Read the motor RAW position based on the `RVAL` (Raw Desired Value) field of Motor Record Returns ------- `double` """ return self.getRawRealPosition()
[docs] def getRawRealPosition(self): """ Read the motor RAW real position based on the `RRBV` (Raw Readback Value) field of Motor Record Returns ------- `double` """ global mtrDB global AR global TR exec(self.__defineMotors()) dformula = self.backFormula.replace("A[", "AR[").replace("T[","TR[") return eval(dformula)
[docs] def getDialRealPosition(self): """ Read the motor DIAL real position based on the `DRBV` (Dial Readback Value) field of virtual Motor Returns ------- `double` """ global mtrDB global AD global TD exec(self.__defineMotors()) dformula = self.backFormula.replace("A[", "AD[").replace("T[","TD[") return eval(dformula)
[docs] def getDialPosition(self): """ Read the motor target DIAL position based on the `DVAL` (Dial Desired Value) field of virtual Motor Returns ------- `double` """ return self.getDialRealPosition()
[docs] def getPosition(self): """ Read the motor target position based on the `VAL` (User Desired Value) field of virtual Motor Returns ------- `double` """ return self.getRealPosition()
[docs] def getEGU(self): """ Read the motor engineering unit based on the `EGU` (Engineering Units) field of the virtual Motor Returns ------- `string` """ return self.virtualEGU
[docs] def getLVIO(self): """ Read the motor limit violation `LVIO` (Limit Violation) field of the virtual Motor Returns ------- `short` """ pass
[docs] def setEGU(self, unit): """ Set the motor engineering unit to the `EGU` (Engineering Units) field of virtual Motor Parameters ---------- unit : `string` The desired engineering unit. .. note:: **Example:** "mm.", "deg." """ self.virtualEGU = unit
[docs] def setHighLimitValue(self, val): """ Set the motor high limit based on the `HLM` (User High Limit) field of virtual Motor Parameters ---------- val : `double` The desired value to set """ pass
[docs] def setLowLimitValue(self, val): """ Set the motor low limit based on the `LLM` (User Low Limit) field of virtual Motor Parameters ---------- val : `double` The desired value to set """ pass
[docs] def setDialHighLimitValue(self, val): """ Set the motor dial high limit based on the `DHLM` (Dial High Limit) field of virtual Motor Parameters ---------- val : `double` The desired value to set """ pass
[docs] def setDialLowLimitValue(self, val): """ Set the motor dial low limit based on the `DLLM` (Dial Low Limit) field of virtual Motor Parameters ---------- val : `double` The desired value to set """ pass
[docs] def setSETMode(self): """ Put the motor in SET mode .. note:: Motor will **NOT** move until it is in in **USE mode** """ for m in self.forwardDict: mtrDB[m].setSETMode()
[docs] def setUSEMode(self): """ Put the motor in **USE mode** """ for m in self.forwardDict: mtrDB[m].setUSEMode()
[docs] def setVariableOffset(self, val): """ Set the motor variable offset based on the `VOF` (Variable Offset) field of virtual Motor Parameters ---------- val : `integer` The desired value to set """ pass
[docs] def setFreezeOffset(self, val): """ Set the motor freeze offset based on the `FOF` (Freeze Offset) field of virtual Motor Parameters ---------- val : `integer` The desired value to set """ pass
[docs] def setOffset(self, val): """ Set the motor offset based on the `OFF` (User Offset, `EGU`) field of virtual Motor Parameters ---------- val : `double` The desired value to set """ global mtrDB for m in self.forwardDict: mtrDB[m].setSETMode() self.setAbsolutePosition(val) for m in self.forwardDict: mtrDB[m].setUSEMode()
[docs] def setDialPosition(self, pos, waitComplete=False): """ Set the motor target DIAL position based on the `DVAL` (Dial Desired Value) field of virtual Motor Parameters ---------- pos : `double` The desired position to set waitComplete : `boolean` (default is **False**) .. note:: If **True**, the function will wait until the movement finish to return, otherwise don't. """ pass
[docs] def setAbsolutePosition(self, pos, waitComplete=False): """ Move the motor to an absolute position received by an input parameter Parameters ---------- pos : `double` The desired position to set waitComplete : `boolean` (default is **False**) .. note:: If **True**, the function will wait until the movement finish to return, otherwise don't. """ global mtrDB global A global T exec(self.__defineMotors()) ca.poll(evt=0.05) ret, msg = self.canPerformMovement(pos) if(not ret): raise Exception("Can't move motor "+self.name+" to desired position: "+str(pos)+ ", " + msg) for m in self.forwardDict: mtrDB[m].setAbsolutePosition(T[m]) if(waitComplete): self.wait()
[docs] def setRelativePosition(self, pos, waitComplete=False): """ Move the motor a distance, received by an input parameter, to a position relative to that current one Parameters ---------- pos : `double` The desired distance to move based on current position waitComplete : `boolean` (default is **False**) .. note: If **True**, the function will wait until the movement finish to return, otherwise don't. """ newPos = self.getRealPosition() + pos self.setAbsolutePosition(newPos, waitComplete)
[docs] def setVelocity(self, velo): """ Set the motor velocity up based on the `VELO` (Velocity, EGU/s) field from virtual Motor Parameters ---------- velo : `double` The desired velocity to set """ pass
[docs] def setAcceleration(self, accl): """ Set the motor acceleration time based on the `ACCL` (Seconds to Velocity) field from virtual Motor Parameters ---------- accl : `double` The desired acceleration to set """ pass
[docs] def setUpdateRequest(self,val): """ Set the motor update request flag based on the `STUP` (Status Update Request) field from virtual Motor Parameters ---------- val : `integer` The desired value to set for the flag """ pass
[docs] def validateLimits(self): """ Verify if motor is in a valid position. In the case it has been reached the HIGH or LOW limit switch, an exception will be raised. """ message = "" if(self.isAtHighLimitSwitch()): message = 'Motor: '+self.name+' reached the HIGH limit switch.' elif(self.isAtLowLimitSwitch()): message = 'Motor: '+self.name+' reached the LOW limit switch.' if(message != ""): raise Exception(message)
def canPerformMovementCalc(self, target): return self.canPerformMovement(target)
[docs] def canPerformMovement(self, target): """ Check if a movement to a given position is possible using the limit values and backlash distance Returns ------- `boolean` .. note:: - **True** -- Motor CAN perform the desired movement; - **False** -- Motor **CANNOT** perform the desired movement. """ global mtrDB global A global T exec(self.__defineMotors()) T = motorTarget() T[self.name] = target for m in self.forwardDict: mPos = eval(self.forwardDict[m]) if(mtrDB[m].canPerformMovementCalc(mPos)): T[m] = mPos else: return False, "Motor "+m+" cannot move to: "+str(mPos) return True,""
[docs] def stop(self): """ Stop the motor """ global mtrDB for m in self.forwardDict: mtrDB[m].stop()
[docs] def wait(self): """ Wait until the motor movement finishes """ while(self.isMoving()): ca.poll(evt=0.01)
def __defineMotors(self): """ Define a set of virtual motors based on devices in the global `mtrDB` Returns ------- `string` A command which combines all devices in `mtrDB` """ global mtrDB cmd = '\n'.join(['%s = "%s"' % (m, m) for m in mtrDB]) return cmd
[docs] def getValue(self): """ Get the current position of the motor. See :class:`py4syn.epics.IScannable` Returns ------- `double` Read the current value (virtual Motor Real Position) """ return self.getRealPosition()
[docs] def setValue(self, v): """ Set the desired motor position. See :class:`py4syn.epics.IScannable` Parameters ---------- v : `double` The desired value (Absolute Position) to set """ self.setAbsolutePosition(v)
A = motorPosition() T = motorTarget() AD = motorPositionDial() TD = motorTargetDial() AR = motorPositionRaw() TR = motorTargetRaw()