From 6d3f5ff028f34fdc97c5560cbbe6c90b2c7caa72 Mon Sep 17 00:00:00 2001
From: "FR@29iduser" <rodolakis@anl.gov>
Date: Mon, 12 Sep 2022 17:09:37 -0500
Subject: [PATCH] changed iex_vpu

---
 iexcode.egg-info/SOURCES.txt          |   1 +
 iexcode/cheatsheet.txt                |  15 +-
 iexcode/instruments/IEX_VPU.py        | 203 +++++-----
 iexcode/instruments/IEX_VPU_backup.py | 544 ++++++++++++++++++++++++++
 4 files changed, 648 insertions(+), 115 deletions(-)
 create mode 100644 iexcode/instruments/IEX_VPU_backup.py

diff --git a/iexcode.egg-info/SOURCES.txt b/iexcode.egg-info/SOURCES.txt
index 2b4744d..28247ed 100644
--- a/iexcode.egg-info/SOURCES.txt
+++ b/iexcode.egg-info/SOURCES.txt
@@ -23,6 +23,7 @@ iexcode/instruments/Lakeshore_335.py
 iexcode/instruments/Logfile.py
 iexcode/instruments/MPA.py
 iexcode/instruments/Motors.py
+iexcode/instruments/Octupole.py
 iexcode/instruments/Scienta.py
 iexcode/instruments/VLS_PGM.py
 iexcode/instruments/__init__.py
diff --git a/iexcode/cheatsheet.txt b/iexcode/cheatsheet.txt
index 2cc6961..668cf78 100644
--- a/iexcode/cheatsheet.txt
+++ b/iexcode/cheatsheet.txt
@@ -99,10 +99,19 @@ BL_mda_filepath => returns the file path in mda scanRecord
 get_all => gets relavent pv values
 
 #IEX_VPU: undulator functions
+ID_calc_SP => calculate the set point for maximum flux
+ID_wait_for_permission => waits for security access 
 ID_get_all => gets ID_mode, ID_QP_ratio, ID_sp, ID_rbv
-ID_off => turns off the ID 
-ID_start => turns on the ID
-ID_restart => turns off the ID and then turns it on with the same set point
+ID_energy_range => return max/min for a given mode
+ID_ready => looks at the two busy records
+ID_off/on => turns off/on the ID 
+ID_power_status => returns off/on
+ID_start => turns on the ID in a given mode
+ID_switch_mode => changes the ID mode
+ID_get/set => gets rbv / sets val in keV
+ID_energy_set => set calibrated energy in eV
+ID_rbv_get
+
 
 #kappa_angle_calcs:
 fourc2kappa => converts between four-circle angles and kappa angles
diff --git a/iexcode/instruments/IEX_VPU.py b/iexcode/instruments/IEX_VPU.py
index 45ddf3a..f368beb 100644
--- a/iexcode/instruments/IEX_VPU.py
+++ b/iexcode/instruments/IEX_VPU.py
@@ -16,14 +16,36 @@ IDcal_path="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Diction
 ################################             ID limits and calibration             ##############################
 ##############################################################################################################
 
-def ID_calc_SP(mono_grating,ID_mode,hv_eV,QP_ratio):    # Mode = state (0=RCP,1=LCP,2=V,3=H)
+pv = 'ID29:'
+pvs={'AccessSecurity':pv+'AccessSecurity.VAL',
+    'mode_rbv':pv+'ActualMode',
+    'mode_val':pv+'DesiredMode.VAL',
+    'check_ready':pv+'feedback.VAL',
+    'check_busy':pv+'BusyRecord',
+    'busy_reset':pv+'Busy.VAL',
+    'main_power':pv+'Main_on_off.VAL',
+    'energy_rbv':pv+'Energy.VAL',
+    'energy_sp':pv+'EnergySet.VAL',
+    'start_ramp':pv+'StartRamp.VAL',
+    'energy_eV_rbv':pv+'EnergyRBV',
+    'energy_eV_sp':pv+'EnergyScanSet.VAL',
+    'scan_eV_rbv':pv+'EnergyScanSeteV',
+    'scan_eV_val':pv+'EnergySetRBV',
+    'table_dir':pv+'TableDirection',
+    'By_q':pv+'ByqRdbk',
+    'Bx_q':pv+'BxqRdbk',
+    'Vcoil':pv+'ByRdbk.VAL',
+    'Hcoil':pv+'BxRdbk.VAL',
+        'QP_ratio':pv+'QuasiRatio.RVAL',}
+
+
+
+def ID_calc_eV(mono_grating,ID_mode,hv_eV,QP_ratio):    # Mode = state (0=RCP,1=LCP,2=V,3=H)
     """Calculate the ID SP for a given polarization mode and energy;
     with Mode = 0 (RCP),1 (LCP), 2 (V), 3 (H)
     
     Previously: ID_Calc
-    """
-    
-    
+    """   
     if type(ID_mode)== str:
         ID_state=ID_state_mode(ID_mode)
     try:
@@ -32,7 +54,7 @@ def ID_calc_SP(mono_grating,ID_mode,hv_eV,QP_ratio):    # Mode = state (0=RCP,1=
     except KeyError:
         message_string='Not a valid ID mode!'+"\nValid Modes: "+str(ID_mode_list())
         print_warning_message(message_string)
-        ID=caget("ID29:EnergySeteV")
+        ID=caget(pvs['energy_rbv'])
     return round(ID,1)
 
 ##############################################################################################################
@@ -48,7 +70,7 @@ def ID_wait_for_permission():
     Previously: WaitForPermission
     """
     while True:
-        ID_Access=caget("ID29:AccessSecurity.VAL")
+        ID_Access=caget(pvs['AccessSecurity'])
         if (ID_Access!=0):
             print("Checking ID permission, please wait..."+dateandtime())
             sleep(30)
@@ -66,8 +88,8 @@ def ID_get_all(verbose=False):
     vals={    
         "ID_mode":ID_mode_get(verbose=False),
         "ID_QP_ratio":ID_QP_ratio_get(verbose=False),
-        "ID_sp":ID_SP_get_eV(verbose=False),
-        "ID_rbv":ID_rbv_get_eV(verbose=False)
+        "ID_sp":caget(pvs['energy_sp']),
+        "ID_rbv":ID_get_eV(verbose=False)
     }
 
     if verbose:
@@ -82,14 +104,12 @@ def ID_energy_range(ID_mode=None):
     Previously: ID_Range
     """
     if ID_mode == None:
-        ID_state=caget("ID29:ActualMode")
+        ID_state=caget(pvs['mode_rbv'])
     else:
         ID_state = ID_state_mode(ID_mode)
     #       RCP,LCP, V , H , HN
-    ID_min_SP = [400,400,440,250,250]
-    ID_max_SP = 3800
-    #hv_min=[390,390,430,245,245]
-
+    ID_min_SP = [.400,.400,.440,.250,.250]
+    ID_max_SP = 3.800
     return ID_min_SP[ID_state],ID_max_SP
 
 def ID_mode_list():
@@ -120,14 +140,14 @@ def ID_state_get():
     """
     Returns the current ID state
     """
-    ID_state = caget("ID29:ActualMode")
+    ID_state = caget(pvs['mode_rbv'])
     return ID_state
 
 def ID_mode_get(verbose=True):
     """
     Returns the current ID mode
     """
-    ID_state = caget("ID29:ActualMode")
+    ID_state =ID_state_get()
     ID_mode = ID_mode_list()[ID_state]
 
     if verbose:
@@ -138,7 +158,7 @@ def ID_mode_set(ID_mode):
     """
     writes the desired mode to the correct ID pv
     """
-    caput("ID29:DesiredMode.VAL",ID_mode,wait=True,timeout=18000)     # RCP
+    caput(pvs['mode_val'],ID_mode,wait=True,timeout=18000)     # RCP
 
 
 def ID_ready(verbose=True):
@@ -148,13 +168,13 @@ def ID_ready(verbose=True):
     Previously: ID_Ready
     """
     while True:
-        RBV=caget("ID29:Energy.VAL")
-        checkready=caget("ID29:feedback.VAL")
-        checkbusy=caget("ID29:BusyRecord")
+        RBV=caget(pvs['energy_rbv'])
+        checkready=caget(pvs['check_ready'])
+        checkbusy=caget(pvs['check_busy'])
         if (checkready!="Ready") or (RBV < 3.7):
             sleep(2)
         elif ((checkready=="Ready") and (RBV > 3.7)) and (checkbusy=="Busy"):
-            caput("ID29:Busy.VAL",0)
+            caput(pvs['busy_reset'],0)
         else:
             if verbose:
                 print("ID Ready")      
@@ -164,7 +184,7 @@ def ID_power_status():
     """
     gets if the ID power supplies are On or Off
     """
-    ID_OnOff=caget('ID29:Main_on_off.VAL')
+    ID_OnOff=caget(pvs['main_power'])
     if ID_OnOff == 1: 
         return 'Off'
     elif ID_OnOff == 0:
@@ -175,7 +195,7 @@ def ID_off(verbose=True):
     waits for permission then turns on the main coils OFF    
     """
     ID_wait_for_permission()
-    caput("ID29:Main_on_off.VAL",1,wait=True,timeout=18000)
+    caput(pvs['main_power'],1,wait=True,timeout=18000)
     sleep(5)
     if verbose:
         print("ID is now off")
@@ -185,12 +205,13 @@ def ID_on(verbose=True):
     """
     waits for permission then turns on the main coils On
     """
+    
     ID_wait_for_permission()
     if verbose:
         print("Starting ID  -  "+dateandtime())
     
-    caput("ID29:EnergySet.VAL",3.8)
-    caput("ID29:Main_on_off.VAL",0,wait=True,timeout=18000)
+    caput(pvs['energy_sp'],3.8)
+    caput(pvs['main_power'],0,wait=True,timeout=18000)
 
 def ID_start(ID_mode='RCP',QP_ratio=None, verbose=True):
     """
@@ -241,58 +262,40 @@ def ID_switch_mode(ID_mode):
     except:
         print_warning_message("Not a valid ID mode")
 
-def ID_SP_get(verbose=False):
-    """
-    returns the ID setpoint in keV
-    """
-    ID_SP = caget("ID29:EnergyScanSet.VAL")
-    if verbose:
-        print("ID_SP: ", ID_SP)
-    return ID_SP
-
-def ID_SP_get_eV(verbose=False):
-    """
-    returns the ID setpoint in eV
-    """
-    ID_SP = ID_SP_get(verbose=False)
-    if verbose:
-        print("ID_SP: ", ID_SP)
-    return ID_SP
-
-def ID_rbv_get(verbose=False):
+def ID_get(verbose=False):
     """
     returns the readback value for the 
     """
-    ID_RBV = caget("ID29:EnergyRBV")
+    ID_RBV = caget(pvs['energy_rbv'])
     if verbose:
         print("ID_RBV: ", ID_RBV)
     return ID_RBV  
 
-def ID_rbv_get_eV(verbose=False):
+def ID_get_eV(verbose=False):
     """
     returns the readback value for the 
     """
-    ID_RBV = ID_rbv_get(verbose=False)
+    ID_RBV = ID_get(verbose=False)*1000
     if verbose:
         print("ID_RBV: ", ID_RBV)
     return ID_RBV 
 
 
-def ID_SP_set(hv_eV,verbose=True):
+def ID_set(keV,verbose=True):
     """
     "Sets the ID set point to a specific value (hv(eV)) which will not likely be optimum"
 
     Previously: SetID_Raw
     """
     def _ID_write_SP_proc(keV):
-        caput("ID29:EnergyScanSet.VAL",keV,wait=True,timeout=18000)
+        caput(pvs['energy_sp'],keV,wait=True,timeout=18000)
         sleep(0.5)
-        caput("ID29:EnergyScanSet.VAL",(keV+0.001),wait=True,timeout=18000)
-        caput('ID29:StartRamp.VAL',1)
+        caput(pvs['start_ramp'],1)
 
-    def _ID_bw_ok(ID_SP):
-        ID_bw = ID_SP*0.095
-        ID_diff = abs(ID_rbv_get()-ID_SP)
+    def _ID_bw_ok(keV,verbose=False):
+        ID_SP = caget(pvs['energy_sp'])
+        ID_bw = ID_SP * 0.095
+        ID_diff = abs(ID_get()-keV)
         if ID_diff > ID_bw:
             return False
         else:
@@ -304,56 +307,31 @@ def ID_SP_set(hv_eV,verbose=True):
 
     #checking if desired is with allowed range, if not printing the nearest allowed value
     ID_max,ID_min = ID_energy_range()
-    ID_SP=min(max(hv_eV,ID_min),ID_max)*1.0
-    if hv_eV < ID_min or hv_eV > ID_max:
+    if keV < ID_min or keV > ID_max:
         message_string="Set point out of BL energy range \nPlease select a different energy."
-        message_string+="\nClosest allowed value is "+str(ID_SP)
+        message_string+="\nClosest allowed value is "+str(min(max(keV,ID_min),ID_max))
         print_warning_message(message_string)
     else:
         #desired energy is within range
-        ID_SP_RBV=round(caget("ID29:EnergySet.VAL"),3)*1000
-        ID_RBV=caget("ID29:EnergyRBV")
-        if ID_SP == ID_SP_RBV:
-            #ID is already at the SP energy
-            if verbose:
-                print("ID SET : "+"%.1f" % ID_SP, "eV")
-                print("ID RBV : "+"%.1f" % ID_RBV, "eV")
-                print(caget('ID29:TableDirection',as_string=True))
-             
-        else:
-            # ID not at energy
-            ready = ID_ready(verbose=False)
-            if ready:
-                #set the energy
-                _ID_write_SP_proc(ID_SP/1000)
-
-                while not ID_ready(verbose=False):
-                    sleep(20)
-
-                #is ID within some bandwidth
-                if _ID_bw_ok(ID_SP) == False:
-                    sleep(20)
-                    if _ID_bw_ok(ID_SP) == False:
-                        print("\nID RBV : "+"%.1f" % ID_RBV, "eV")
-                        ID_start(ID_mode_get())
-                        if ID_ready(verbose=False):
-                            #set the energy
-                            _ID_write_SP_proc(ID_SP/1000)
-               
-    if verbose:
-        print("\nID SET : "+"%.1f" % ID_SP, "eV") 
-        print("ID RBV : "+"%.1f" % ID_RBV, "eV")
-        print(caget('ID29:TableDirection',as_string=True))    
-
-        if ID_QP_ratio_get()[0] < 100:
-            #check ratio
-            Byq=caget("ID29:ByqRdbk")
-            Vcoil=caget("ID29:ByRdbk.VAL")
-            ratio=Byq/Vcoil
-            ratio_RBV=caget("ID29:QuasiRatio.RVAL")
-            if verbose:
-                print("QP ratio =", round(ratio,3)*100,"%")
-                print("QP RBV   =", ratio_RBV,"%")    
+        _ID_write_SP_proc(keV)
+        # ID not at energy
+        ready = ID_ready(verbose=False)
+        if ready:
+            #set the energy
+            _ID_write_SP_proc(ID_SP/1000)
+            while not ID_ready(verbose=False):
+                sleep(20)
+            #is ID within some bandwidth
+            if _ID_bw_ok(ID_SP,verbose=verbose):
+                if verbose:
+                    ID_SP = caget(pvs['energy_sp'])
+                    ID_RBV = ID_get()
+                    print("ID SET : "+"%.1f" % ID_SP, "eV")
+                    print("ID RBV : "+"%.1f" % ID_RBV, "eV")
+                    print(caget(pvs['table_dir'],as_string=True))
+            else:
+                ID_QP_ratio_get(verbose)
+ 
 
 def ID_energy_set(hv_eV,QP_ratio=None):
     """
@@ -366,8 +344,8 @@ def ID_energy_set(hv_eV,QP_ratio=None):
     """
     ID_mode = ID_mode_list()[ID_state_get()]
     mono_grating = mono_grating_get()
-    ID_SP = ID_calc_SP(mono_grating,ID_mode,hv_eV,QP_ratio)
-    ID_SP_set(ID_SP)
+    keV = ID_calc_eV(mono_grating,ID_mode,hv_eV,QP_ratio)/1000.0
+    ID_set(keV)
 
     
 def ID_QP_ratio_get(verbose=True):
@@ -376,13 +354,14 @@ def ID_QP_ratio_get(verbose=True):
     calculate the QP ratio
 
     """
-    Byq=caget("ID29:ByqRdbk")
-    Vcoil=caget("ID29:ByRdbk.VAL")
-    ratio_calc=Byq/Vcoil
-    ratio_RBV=caget("ID29:QuasiRatio.RVAL")
+    Byq=caget(pvs['By_q'])
+    Vcoil=caget(pvs['Vcoil'])
+    ratio_calc=round(Byq/Vcoil,3)*100
+    ratio_RBV=caget(pvs['QP_ratio'])
     if verbose:
-        print("QP ratio =", round(ratio_RBV,3)*100,"%")
         print("QP RBV   =", ratio_RBV,"%")
+        print("QP calculated ratio =", ratio_calc ,"%")
+        
 
     if abs(ratio_RBV-ratio_calc)>1:
         message_string="QP RBV and QP calc do not agree \nCheck Interlocks"
@@ -479,9 +458,9 @@ def ID_scan_pvs():
     """ 
     returns the rbv and val for scanning 
     """
-    val_pv="ID29:EnergyScanSeteV"
-    rbv_pv="ID29:EnergySetRBV"
-    return val_pv, rbv_pv
+    val_pv=pvs['scan_eV_val']
+    rbv_pv=pvs['scan_eV_rbv']
+    return rbv_pv, val_pv
 
 def ID_scan_fillin(mda,scan_dim,start,stop,step,**kwargs):
     """
@@ -491,7 +470,7 @@ def ID_scan_fillin(mda,scan_dim,start,stop,step,**kwargs):
     """
     #Setting up the ScanRecord for ID in Table mode
     val_pv, rbv_pv = ID_scan_pvs()
-    mda.fillin(scan_dim,val_pv,rbv_pv,start,stop,step,**kwargs)
+    mda.fillin(scan_dim,rbv_pv,val_pv,start,stop,step,**kwargs)
 
 
 def ID_scan_fillin_table(mda,scan_dim,ID_array,**kwargs):
@@ -501,8 +480,8 @@ def ID_scan_fillin_table(mda,scan_dim,ID_array,**kwargs):
     **kwargs => scanRecord.fillin kwargs
     """
     #Setting up the ScanRecord for ID in Table mode
-    val_pv, rbv_pv = ID_scan_pvs()
-    mda.fillin.table(scan_dim,val_pv,rbv_pv,ID_array,**kwargs)
+    rbv_pv, val_pv = ID_scan_pvs()
+    mda.fillin.table(scan_dim,rbv_pv,val_pv,ID_array,**kwargs)
 
 ##############################################################################################################
 ##############################             ID direction table        ##############################
diff --git a/iexcode/instruments/IEX_VPU_backup.py b/iexcode/instruments/IEX_VPU_backup.py
new file mode 100644
index 0000000..57ff364
--- /dev/null
+++ b/iexcode/instruments/IEX_VPU_backup.py
@@ -0,0 +1,544 @@
+from os import path
+from math import *
+from time import sleep
+import numpy.polynomial.polynomial as poly
+
+from epics import caget, caput
+
+from iexcode.instruments.userCalcs import userCalcOut_clear
+from iexcode.instruments.utilities import dateandtime, print_warning_message, read_dict
+from iexcode.instruments.VLS_PGM import mono_grating_get
+from iexcode.instruments.shutters import main_shutter_open
+
+IDcal_path="/home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries"
+
+##############################################################################################################
+################################             ID limits and calibration             ##############################
+##############################################################################################################
+
+def ID_calc_SP(mono_grating,ID_mode,hv_eV,QP_ratio):    # Mode = state (0=RCP,1=LCP,2=V,3=H)
+    """Calculate the ID SP for a given polarization mode and energy;
+    with Mode = 0 (RCP),1 (LCP), 2 (V), 3 (H)
+    
+    Previously: ID_Calc
+    """
+    
+    
+    if type(ID_mode)== str:
+        ID_state=ID_state_mode(ID_mode)
+    try:
+        K=ID_coef(mono_grating,ID_state,hv_eV,QP_ratio)
+        ID=poly.polyval(hv_eV,K)
+    except KeyError:
+        message_string='Not a valid ID mode!'+"\nValid Modes: "+str(ID_mode_list())
+        print_warning_message(message_string)
+        ID=caget("ID29:EnergySeteV")
+    return round(ID,1)
+
+##############################################################################################################
+################################             ID Functions             ##############################
+##############################################################################################################
+
+
+def ID_wait_for_permission():
+    """
+    Monitors the ID permissions and waits for the ID to be in User Mode and then breaks
+    Checks the status every 30 seconds
+
+    Previously: WaitForPermission
+    """
+    while True:
+        ID_Access=caget("ID29:AccessSecurity.VAL")
+        if (ID_Access!=0):
+            print("Checking ID permission, please wait..."+dateandtime())
+            sleep(30)
+        else:
+            print("ID now in user mode -"+dateandtime())
+            break
+
+##############################################################################################################
+def ID_get_all(verbose=False):
+    """ 
+    returns dictionary with: ID_Mode, ID_QP_ratio, ID_SP, ID_RBV
+
+    Previously: Get_energy
+    """
+    vals={    
+        "ID_mode":ID_mode_get(verbose=False),
+        "ID_QP_ratio":ID_QP_ratio_get(verbose=False),
+        "ID_sp":ID_SP_get_eV(verbose=False),
+        "ID_rbv":ID_rbv_get_eV(verbose=False)
+    }
+
+    if verbose:
+        print(" ID SP  : "+"%.2f" % vals["ID_sp"] , "eV    ID mode : "+vals["ID_mode"])
+        print(" ID RBV : "+"%.2f" % vals['ID_rbv'], "eV    QP mode : "+str(vals['ID_QP_ratio']) +" %")
+    return vals
+
+def ID_energy_range(ID_mode=None):  
+    """
+    Returns the ID_min_SP, ID_max_SP for a given ID mode
+    
+    Previously: ID_Range
+    """
+    if ID_mode == None:
+        ID_state=caget("ID29:ActualMode")
+    else:
+        ID_state = ID_state_mode(ID_mode)
+    #       RCP,LCP, V , H , HN
+    ID_min_SP = [400,400,440,250,250]
+    ID_max_SP = 3800
+    #hv_min=[390,390,430,245,245]
+
+    return ID_min_SP[ID_state],ID_max_SP
+
+def ID_mode_list():
+    """
+    returns the ID_mode_List
+    current mode = 
+    """
+    return ["RCP", "LCP", "V", "H", "HN"]
+
+
+def ID_state_mode(ID_mode):
+    """ 
+    Returns the state number if mode is a string 
+    Returns the mode string if mode is an int or float
+
+    Previously: ID_State2Mode(which,mode)
+    """
+    try:
+        ID_mode = ID_mode.upper()
+        ID_state =ID_mode_list().index(ID_mode)
+        return ID_state
+
+    except:
+        message_string='Not a valid ID mode!'+"\nValid Modes: "+str(ID_mode_list())
+        print_warning_message(message_string)
+
+def ID_state_get():
+    """
+    Returns the current ID state
+    """
+    ID_state = caget("ID29:ActualMode")
+    return ID_state
+
+def ID_mode_get(verbose=True):
+    """
+    Returns the current ID mode
+    """
+    ID_state = caget("ID29:ActualMode")
+    ID_mode = ID_mode_list()[ID_state]
+
+    if verbose:
+        print('ID mode: '+ID_mode)
+    return ID_mode
+
+def ID_mode_set(ID_mode):
+    """
+    writes the desired mode to the correct ID pv
+    """
+    caput("ID29:DesiredMode.VAL",ID_mode,wait=True,timeout=18000)     # RCP
+
+
+def ID_ready(verbose=True):
+    """
+    check both the read and busy pv every 2 seconds
+
+    Previously: ID_Ready
+    """
+    while True:
+        RBV=caget("ID29:Energy.VAL")
+        checkready=caget("ID29:feedback.VAL")
+        checkbusy=caget("ID29:BusyRecord")
+        if (checkready!="Ready") or (RBV < 3.7):
+            sleep(2)
+        elif ((checkready=="Ready") and (RBV > 3.7)) and (checkbusy=="Busy"):
+            caput("ID29:Busy.VAL",0)
+        else:
+            if verbose:
+                print("ID Ready")      
+            return True 
+
+def ID_power_status():
+    """
+    gets if the ID power supplies are On or Off
+    """
+    ID_OnOff=caget('ID29:Main_on_off.VAL')
+    if ID_OnOff == 1: 
+        return 'Off'
+    elif ID_OnOff == 0:
+        return 'On' 
+
+def ID_off(verbose=True):
+    """
+    waits for permission then turns on the main coils OFF    
+    """
+    ID_wait_for_permission()
+    caput("ID29:Main_on_off.VAL",1,wait=True,timeout=18000)
+    sleep(5)
+    if verbose:
+        print("ID is now off")
+
+
+def ID_on(verbose=True):
+    """
+    waits for permission then turns on the main coils On
+    """
+    ID_wait_for_permission()
+    if verbose:
+        print("Starting ID  -  "+dateandtime())
+    
+    caput("ID29:EnergySet.VAL",3.8)
+    caput("ID29:Main_on_off.VAL",0,wait=True,timeout=18000)
+
+def ID_start(ID_mode='RCP',QP_ratio=None, verbose=True):
+    """
+    waits for ID permission and then 
+    starts ID with a specific polarization
+    mode = \"H\", \"V\", \"RCP\" or \"LCP\"
+    QP ratio if specified
+    """
+    #turns on if ID is off
+    if ID_power_status =='Off':
+        ID_on()
+    #set QP 
+    if ID_ready():
+        ID_QP_mode_set(QP_ratio,verbose)
+    #set ID mode
+    if ID_ready():
+        ID_mode_set(ID_mode, verbose)
+    #opens the main shutter
+    if ID_ready():
+        main_shutter_open()
+
+    if verbose:
+        print('ID is now on, please set your energy')
+
+def ID_switch_mode(ID_mode):
+    """
+    Change ID polarization; which = 'H', 'V', 'RCP' or 'LCP'
+        if ID_mode = current mode then does nothing
+
+    WARNING: Does not set the energy
+
+    Previously Switch_IDMode
+    """
+    ID_wait_for_permission()
+    main_shutter_open()
+    ID_state = ID_state_get()
+
+    try:
+        if ID_state_mode(ID_mode) != ID_state:
+            print("Turning ID off...")
+            ID_off(verbose=True)
+            sleep(10)
+            
+            print("Switching ID mode, please wait...")
+            ID_mode_set(ID_mode) 
+            ID_ready()
+        print("ID Mode:",ID_mode)
+    except:
+        print_warning_message("Not a valid ID mode")
+
+def ID_SP_get(verbose=False):
+    """
+    returns the ID setpoint in keV
+    """
+    ID_SP = caget("ID29:EnergyScanSet.VAL")
+    if verbose:
+        print("ID_SP: ", ID_SP)
+    return ID_SP
+
+def ID_SP_get_eV(verbose=False):
+    """
+    returns the ID setpoint in eV
+    """
+    ID_SP = ID_SP_get(verbose=False)
+    if verbose:
+        print("ID_SP: ", ID_SP)
+    return ID_SP
+
+def ID_rbv_get(verbose=False):
+    """
+    returns the readback value for the 
+    """
+    ID_RBV = caget("ID29:EnergyRBV")
+    if verbose:
+        print("ID_RBV: ", ID_RBV)
+    return ID_RBV  
+
+def ID_rbv_get_eV(verbose=False):
+    """
+    returns the readback value for the 
+    """
+    ID_RBV = ID_rbv_get(verbose=False)
+    if verbose:
+        print("ID_RBV: ", ID_RBV)
+    return ID_RBV 
+
+
+def ID_SP_set(hv_eV,verbose=True):
+    """
+    "Sets the ID set point to a specific value (hv(eV)) which will not likely be optimum"
+
+    Previously: SetID_Raw
+    """
+    def _ID_write_SP_proc(keV):
+        caput("ID29:EnergyScanSet.VAL",keV,wait=True,timeout=18000)
+        sleep(0.5)
+        caput("ID29:EnergyScanSet.VAL",(keV+0.001),wait=True,timeout=18000)
+        caput('ID29:StartRamp.VAL',1)
+
+    def _ID_bw_ok(ID_SP):
+        ID_bw = ID_SP*0.095
+        ID_diff = abs(ID_rbv_get()-ID_SP)
+        if ID_diff > ID_bw:
+            return False
+        else:
+            return True
+
+    #checking permissions and opening the main shutter
+    ID_wait_for_permission()
+    main_shutter_open()
+
+    #checking if desired is with allowed range, if not printing the nearest allowed value
+    ID_max,ID_min = ID_energy_range()
+    ID_SP=min(max(hv_eV,ID_min),ID_max)*1.0
+    if hv_eV < ID_min or hv_eV > ID_max:
+        message_string="Set point out of BL energy range \nPlease select a different energy."
+        message_string+="\nClosest allowed value is "+str(ID_SP)
+        print_warning_message(message_string)
+    else:
+        #desired energy is within range
+        ID_SP_RBV=round(caget("ID29:EnergySet.VAL"),3)*1000
+        ID_RBV=caget("ID29:EnergyRBV")
+        if ID_SP == ID_SP_RBV:
+            #ID is already at the SP energy
+            if verbose:
+                print("ID SET : "+"%.1f" % ID_SP, "eV")
+                print("ID RBV : "+"%.1f" % ID_RBV, "eV")
+                print(caget('ID29:TableDirection',as_string=True))
+             
+        else:
+            # ID not at energy
+            ready = ID_ready(verbose=False)
+            if ready:
+                #set the energy
+                _ID_write_SP_proc(ID_SP/1000)
+
+                while not ID_ready(verbose=False):
+                    sleep(20)
+
+                #is ID within some bandwidth
+                if _ID_bw_ok(ID_SP) == False:
+              ID_QP_ratio_get  print("QP ratio =", round(ratio,3)*100,"%")
+                print("QP RBV   =", ratio_RBV,"%")    
+
+def ID_energy_set(hv_eV,QP_ratio=None):
+    """
+    Sets optimum ID set point for hv(eV) (max intensity)
+    and opens the main shutter
+
+    Note that QP is generally not calibrated
+
+    Previously: SetID
+    """
+    ID_mode = ID_mode_list()[ID_state_get()]
+    mono_grating = mono_grating_get()
+    ID_SP = ID_calc_SP(mono_grating,ID_mode,hv_eV,QP_ratio)
+    ID_SP_set(ID_SP)
+
+    
+def ID_QP_ratio_get(verbose=True):
+    """
+    gets the read back for the QP ratio
+    calculate the QP ratio
+
+    """
+    Byq=caget("ID29:ByqRdbk")
+    Vcoil=caget("ID29:ByRdbk.VAL")
+    ratio_calc=Byq/Vcoil
+    ratio_RBV=caget("ID29:QuasiRatio.RVAL")
+    if verbose:
+        print("QP ratio =", round(ratio_RBV,3)*100,"%")
+        print("QP RBV   =", ratio_RBV,"%")
+
+    if abs(ratio_RBV-ratio_calc)>1:
+        message_string="QP RBV and QP calc do not agree \nCheck Interlocks"
+        print_warning_message(message_string)
+    return ratio_RBV, ratio_calc
+
+def ID_QP_mode_set(QP_ratio=None,verbose=True):
+    """
+    switched to QP mode, if not currently in
+    sets the QP ratio (QP ratio min is 70) and polarization (ID mode)
+
+    WARNING: you will need to set the polarization and mode afterward
+
+    Previously: Switch_IDQP
+    """
+
+    #checking if above the minimum allowed value
+    QP_min = 70
+    if QP_ratio < QP_min:
+        message_string="QP ratio is too small, setting it to minimum allowed value ("+str(QP_min)+")"
+        print_warning_message(message_string)
+    QP_ratio=max(70,QP_ratio)
+
+    if QP_ratio != None:
+        #checking to see if already in specified QP
+        ratio_RBV, ratio_calc = ID_QP_ratio_get()
+        #not the same ratio
+        if ratio_RBV != QP_ratio:
+            ID_off()
+            caput("ID29:QuasiRatioIn.C",QP_ratio)
+        
+            ID_on()
+            sleep(15)
+
+            
+def ID_coef(grt,ID_state,hv_eV,QP_ratio):    # Mode = state (0=RCP,1=LCP,2=V,3=H); 
+    
+    """Return the ID coeff for a given polarization mode and energy;
+    with Mode = 0 (RCP),1 (LCP), 2 (V), 3 (H).
+    Current coefficient dictionary:
+        /home/beams22/29IDUSER/Documents/User_Macros/Macros_29id/IEX_Dictionaries/Dict_IDCal.txt
+    
+    Previously: ID_Coef
+    """
+
+    if QP_ratio == None:
+        QP_ratio = ID_QP_ratio_get()[0]
+
+    def ListRange(grt,ID_state,IDdict):  # extract the list of break pts for a given mode/grt 
+        tmp_list=[]
+        for item in (IDdict[grt][ID_state]):
+            tmp_list.append(item[0])  
+        return tmp_list
+
+
+    def FindRange(hv_eV,range_list):         # returns the index for the corresponding range
+        B = [x - hv_eV for x in range_list]
+        #print(B)
+        index = [i for (i, x) in enumerate(B) if x > 0]
+        #print(index)
+        return(index[0])
+
+   
+    
+    if QP_ratio < 100:
+        if isinstance(QP_ratio,float) or isinstance(QP_ratio,int): 
+            QP_ratio=str(floor(QP_ratio))
+            dictfile='Dict_IDCal_QP'+QP_ratio+'.txt'
+            error_msg="calibration curve might not exist for this QP value"
+        else:
+            print('invalid QP value')
+            return
+    else:
+        dictfile='Dict_IDCal.txt' 
+        error_msg=""
+    try:
+        IDcal_fpath = path.join(IDcal_path,dictfile)
+        ID_function=read_dict(IDcal_fpath)
+    
+    except KeyError:
+        print("Unable to read dictionary "+dictfile+" ; "+error_msg) 
+        
+    try:   
+        Lrange = ListRange(grt,ID_state,ID_function)
+        Erange = FindRange(hv_eV,Lrange)
+        K = ID_function[grt][ID_state][Erange][1]
+        return K
+        
+    except KeyError:
+        print("WARNING: PLease select one of the following: "+str(ID_mode_list()))
+
+
+def ID_scan_pvs():
+    """ 
+    returns the rbv and val for scanning 
+    """
+    val_pv="ID29:EnergyScanSeteV"
+    rbv_pv="ID29:EnergySetRBV"
+    return val_pv, rbv_pv
+
+def ID_scan_fillin(mda,scan_dim,start,stop,step,**kwargs):
+    """
+    fills in the scanRecord for scanning the ID set point
+
+    **kwargs => scanRecord.fillin kwargs
+    """
+    #Setting up the ScanRecord for ID in Table mode
+    val_pv, rbv_pv = ID_scan_pvs()
+    mda.fillin(scan_dim,val_pv,rbv_pv,start,stop,step,**kwargs)
+
+
+def ID_scan_fillin_table(mda,scan_dim,ID_array,**kwargs):
+    """
+    fills in the scanRecord for scanning the ID set point
+
+    **kwargs => scanRecord.fillin kwargs
+    """
+    #Setting up the ScanRecord for ID in Table mode
+    val_pv, rbv_pv = ID_scan_pvs()
+    mda.fillin.table(scan_dim,val_pv,rbv_pv,ID_array,**kwargs)
+
+##############################################################################################################
+##############################             ID direction table        ##############################
+##############################################################################################################
+
+def ID_Table():
+    """
+
+    Previously: ID_Table
+    """
+    table = caget("ID29:TableDirection")    # up = 1 , down = 0
+    By = caget("ID29:ByPolaritySet")        # pos = 1, neg = 0
+
+    mode = ID_mode_get()
+
+    if By > 0:
+        print("\nBy > 0")
+        if table == 1:            # By=1, table = 1     => -1    => A=B
+            print("table = up")
+            ID_direction = -1
+        elif table ==  0:        # By=1, table = 0     => +1    => A#B
+            print("table = down")
+            ID_direction = 1
+    elif By <= 0:
+        print("\nBy < 0")
+        if table == 1:            # By=0, table = 1     => +1    => A=B
+            print("table = up")
+            ID_direction = 1
+        elif table ==  0:        # By=0, table = 0     => -1    => A#B
+            print("table = down")
+            ID_direction = -1
+
+
+    if mode == "H" and mode == "RCP":
+        if By > 0 and table == 0:
+            print_warning_message("will do a long hysteresis if decreasing energy !!!")
+#    if Mode == "HN" and Mode == "LCP":
+#        if By = 0 and table == 1:
+#            print "WARNING: will do a long hysteresis if decreasing energy !!!"
+    print("ID direction", ID_direction)
+    return ID_direction
+
+def ID_table_userCalcOut(): 
+    """
+       # Work in progress
+
+    Previously:ID_Table_CalcOut
+    """
+    n=4
+    userCalcOut_clear("b",n)
+    pvstr="29idb:userCalcOut"+str(n)
+    caput(pvstr+".DESC","ID_Table")
+    table="ID29:TableDirection"    # up = 1 , down = 0
+    By="ID29:ByPolaritySet"        # pos = 1, neg = 0
+    caput(pvstr+".INPA",table+" CP NMS")
+    caput(pvstr+".INPB",By+" CP NMS")
+    caput(pvstr+".CALC$","A#B")
+    caput(pvstr+".OOPT","On Change")
+    caput(pvstr+".DOPT","Use CALC")
\ No newline at end of file
-- 
GitLab