From 541feeaf919ed263fab0cb84d0191b9ce1f5d184 Mon Sep 17 00:00:00 2001
From: "FR@29iduser" <rodolakis@anl.gov>
Date: Mon, 12 Sep 2022 13:04:20 -0500
Subject: [PATCH] add octupole

---
 iexcode/instruments/IEX_BL_config.py |  10 +-
 iexcode/instruments/Octupole.py      | 507 +++++++++++++++++++++++++++
 iexcode/instruments/scalers.py       |  14 +
 3 files changed, 526 insertions(+), 5 deletions(-)
 create mode 100644 iexcode/instruments/Octupole.py

diff --git a/iexcode/instruments/IEX_BL_config.py b/iexcode/instruments/IEX_BL_config.py
index 7117b9d..07026ce 100644
--- a/iexcode/instruments/IEX_BL_config.py
+++ b/iexcode/instruments/IEX_BL_config.py
@@ -13,14 +13,14 @@ init kwargs to see which defaults you can change.
 """
 
 #For new endstation modify here:
-endstations_list = ['ARPES','Kappa']   
+endstations_list = ['ARPES','kappa','Octupole']   
 class Beamline_Config:
     """
     used for defining which endstation in which you are running
     for short names and ioc info
 
     BL = Endstation('ARPES')
-        BL.endstation_name: name of endstation  ("ARPES" / "Kappa" / "Octupole")
+        BL.endstation_name: name of endstation  ("ARPES" / "kappa" / "Octupole")
         
         BL.ioc: string of scan ioc  ('29idb:', '29idTest:')
         BL.mda: scanRecord object of ioc specified by .ioc 
@@ -50,7 +50,7 @@ class Beamline_Config:
             BL.folder => 'b','c','d'
             BL.prefix => 'ARPES_','Kappa_'
             BL.ioc => previously: BL_ioc()
-            BL.Motors => motor calls
+            BL.Motors => motor calls#For new endstation modify here:
             BL.safe_state => function to put endstation in a 'safe state'
             BL.endstation_get => function to return endstation status
             BL.log => log class
@@ -96,8 +96,8 @@ class Beamline_Config:
                     self.folder = 'd'
                     self.prefix = 'Kappa_'
                     self.branch = 'd'
-                #elif endstation_name == 'Octupole':
-                    #self.folder = 'e'
+                elif endstation_name == 'Octupole':
+                    self.folder = 'd'
                     self.branch = 'd'
                 else:
                     print('folder and prefix not set')
diff --git a/iexcode/instruments/Octupole.py b/iexcode/instruments/Octupole.py
new file mode 100644
index 0000000..6ff8d9b
--- /dev/null
+++ b/iexcode/instruments/Octupole.py
@@ -0,0 +1,507 @@
+import numpy as np
+from time import sleep
+from math import floor
+
+from epics import caget, caput,PV
+
+import iexcode.instruments.cfg as iex
+from iexcode.instruments.IEX_endstations import Endstation
+from iexcode.instruments.cameras import _enable_endstation_cameras
+
+from iexcode.instruments.staff import staff_detector_dictionary
+from iexcode.instruments.files_and_folders import check_run,make_user_folders,folder_mda
+from iexcode.instruments.staff import staff_detector_dictionary
+from iexcode.instruments.xrays import _xrays_detector_dictionary,_xrays_reset,xrays_get_all
+
+from iexcode.instruments.conversions_constants import *
+from iexcode.instruments.utilities import *
+from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs, userCalcOut_clear
+
+from iexcode.instruments.Motors import Motors
+from iexcode.instruments.current_amplifiers import SRS, ca_reset_all, Keithley_pv
+from iexcode.instruments.valves import branch_valve_close
+from iexcode.instruments.shutters import branch_shutter_close
+
+#branch specific
+from iexcode.instruments.slits import slit3D_get
+#endstation specific
+from iexcode.instruments.scalers import scaler_cts
+from iexcode.instruments.spec_stuff import folders_spec
+
+default_ioc = '29ide:'
+#############################################################################
+def Octupole_init(*userName,**kwargs):
+    """
+    used to intialize the Endstation class which carries scanRecord, logging ... parameters
+    returns global detectors relavent to this endstation (tey,d3,d4,mesh,Kappa_scaler_pv,tth_pv,tthdet)
+
+        *userName is an optional parameter, will be prompted if set_folders=True
+
+        **kwargs:
+            set_folders: sets the mda and EA folders (default => True)  
+            reset: to reset the detectors in the IOC, etc (default => True)  
+            xrays: sets global variable and detectors for x-ray (default => True) 
+            mode: used sets the detectors 'user ' / 'staff' (default => 'user' )  
+     """
+    kwargs.setdefault('scan_ioc',default_ioc)
+    kwargs.setdefault('xrays',True)
+    kwargs.setdefault('mode','user')
+    kwargs.setdefault('set_folders',True)
+    kwargs.setdefault('reset',True)
+
+    #motors
+    physical_motors = ['x','y','z','th','tth']
+    pseudo_motors = ['']
+    global Octupole_Motors
+    Octupole_Motors = Motors('Octupole',_Octupole_motor_dictionary(),physical_motors,pseudo_motors)
+
+    #endstation
+    iex.BL = Endstation('Kappa',kwargs['scan_ioc'],kwargs['xrays'],kwargs['mode'],Octupole_Motors)
+
+    #setting folders
+    if kwargs['set_folders']:
+        if iex.BL.mode == 'staff':
+            user_name = 'staff'
+        else:
+            if len(userName)==0:
+                user_name = input('user name: ')
+            else:
+                user_name = userName[0]
+        print(user_name)
+        folders_Octupole(user_name,**kwargs)
+
+        #update for default scanRecord advanced parameters
+        iex.BL.mda.log('Octupole',user_name,_Octupole_log_header(),_Octupole_log_entries)
+
+    #global detectors
+    global tey,tfy,pd,mesh,diag,kbh,kbv, Octupole_scaler_pv
+    mesh = SRS("29ide:scaler1.S2", '29idd:A4')
+    diag = SRS("29ide:scaler1.S6", '29ide:SR570_1')
+    kbh = SRS("29ide:scaler1.S7", '29ide:SR570_2')
+    kbv = SRS("29ide:scaler1.S8", '29ide:SR570_3')
+    tey = SRS("29ide:scaler1.S3", '29ide:SR570_4')
+    tfy = Scaler("29ide:scaler1.S4")
+    pd = SRS("29idMZ0:scaler1.S5", '29ide:SR570_5')
+    Octupole_scaler_pv = '29ide:scaler1.CNT'
+    
+    #resetting everything
+    if kwargs['reset']:
+        Octupole_reset(**kwargs)
+
+    print ('Octupole initalized')
+    #return any detectors or other parameters that you want access to from jupyter
+    return tey,tfy,pd,mesh,diag,kbh,kbv, Octupole_scaler_pv
+##############################################################################################################
+##############################                    detectors and motors                ##############################
+##############################################################################################################
+def _Octupole_detector_list():
+    """
+    list of detectors to trigger 
+
+    Previously: part of Detector_List
+    """
+    ca_list=[]
+    return ca_list
+
+
+def _Octupole_detector_dictionary(**kwargs):
+    """
+    returns a dictionary of the default detectors
+
+    **kwargs
+    add_vortex: to specifiy to add the vortex detectors to the dictionary (True/False)
+
+    Previously: Detector_Default
+    """
+    kwargs.setdefault('add_vortex',False)
+    det_dict={}
+    vortex={
+        15:"",
+        16:"",
+        17:"",
+    }
+    sample_temp={
+        23:"29idd:LS340_1:TC1:SampleA",
+    }
+    m3r={
+        25:"29id_ps6:Stats1:CentroidX_RBV",
+        26:"29id_ps6:Stats1:SigmaX_RBV",
+        27:"29id_ps6:Stats1:CentroidTotal_RBV",
+    }
+    scalers={
+        31:"29ide:scaler1.S2",
+        32:"29ide:scaler1.S3",
+        33:"29ide:scaler1.S4",
+        34:"29ide:scaler1.S5",
+        35:"29ide:scaler1.S6",
+        36:"29ide:scaler1_calc1.B",
+        37:"29ide:scaler1_calc1.C",
+        38:"29ide:scaler1_calc1.D",
+        39:"29ide:scaler1_calc1.E",
+        40:"29ide:scaler1.S7",
+        41:"29ide:scaler1.S8",
+    }
+    motors={
+        51:"29ide:m11.RBV",
+        52:"29ide:m12.RBV",
+        53:"29ide:m13.RBV",
+        54:"29ide:m14.RBV",
+        55:"29ide:m15.RBV",
+    }
+    #hkl are listed just a place holders, they are filled in by thier scanning functions
+    det_dict.update(sample_temp)
+    det_dict.update(scalers)
+    det_dict.update(motors)
+    if kwargs['add_vortex']:
+        det_dict.update(vortex)
+
+    #add detectors related to beamline
+    if iex.BL.xrays: 
+        det_dict.update(_xrays_detector_dictionary())
+
+    return det_dict
+
+
+def _Octupole_motor_dictionary(name):
+    """
+    motor_dictionary = {name:[rbv,val,spmg,pv]} for physical and psuedo/Euler motors
+    usage:
+        KappaS_PVmotor('x') => ['29idKappa:m2.RBV', '29idKappa:m2.VAL', '29idKapp:m2.SPMG','29idKappa:m2']
+    """
+    motor_nums={
+        'x':11,
+        'y':12,
+        'z':13,
+        'th':14,
+        'tth':15,
+      }
+    motor_dictionary = {}
+    for name in motor_nums.keys():
+        pv = '29ide:m'+str(motor_nums[name])
+        motor_dictionary.update({name:[pv+'.VAL',pv+'.SPMG',pv]})    
+    
+    return motor_dictionary
+
+def Octupole_extra_pvs():
+    """
+    used to get the PV associated with a given pnuemonic
+
+    """
+    d={
+        "TA":"29idd:LS340_1:TC1:Control",      
+    }
+    return d
+
+
+
+#############################################################################################################
+##############################                 setting folder                   ##############################
+##############################################################################################################
+def folders_Octupole(user_name,**kwargs):
+    """
+    Create and sets (if create_only=False) all the folders the current run and ARPES user 
+    Sets the FileName for logging to be "UserName/YYYYMMDD_log.txt using logname_Set()"
+    
+    **kwargs:
+        set_folders = True (default); set the mda and EA scanRecords
+                    = False; only makes the folders (was create_only)
+        run: run cycle e.g. 2022_1 (default => check_run() )
+        ftp = True / False (default); print what needs to be added to the ftps crontab and create additional ftp folders
+        mda_ioc: will overwrite the default ioc for mda scans
+        debug
+    """
+    kwargs.setdefault('set_folders',False)
+    kwargs.setdefault('run',check_run())
+    kwargs.setdefault('ftp',False)
+    kwargs.setdefault('debug',False)
+
+    run = kwargs['run']
+
+    if kwargs['debug']:
+        print("run,folder,user_name,ioc,ftp: ",run,iex.BL.folder,user_name,iex.BL.ioc,kwargs['ftp'])
+    
+    # Create User Folder:
+    make_user_folders(run,iex.BL.folder,user_name,iex.BL.endstation,ftp=kwargs['ftp'])
+    sleep(5)
+
+    if kwargs["set_folders"]:
+        # Set up MDA folder:
+        folder_mda(run,iex.BL.folder,user_name,iex.BL.prefix,iex.BL.ioc)
+
+    #resetting
+    if 'reset':
+        Octupole_reset()
+
+def Octupole_reset():
+    """
+    resets scanRecord, current amplifiers, mono limits and lakeshore
+    """
+    #writing default parameters to iex.BL.mda
+    if iex.BL.mode=='staff':
+        iex.BL.mda.detector_dictionary = staff_detector_dictionary()
+    else:
+         iex.BL.mda.detector_dictionary = _Octupole_detector_dictionary()
+    iex.BL.mda.trigger_dictionary = _Octupole_trigger_dictionary()
+    iex.BL.mda.scan_before_sequence = _Octupole_scan_before_sequence()
+    iex.BL.mda.scan_after_sequence = _Octupole_scan_after_sequence()    
+    
+    #resetting the scanRecord
+    print("resetting the scanRecord - "+iex.BL.ioc)
+    iex.BL.mda.reset_all()
+
+    #resetting the current amplifiers
+    if iex.BL.xray:
+        ca_reset_all()
+    
+    #resetting mono and anyother beamline stuff
+    if iex.BL.xrays:
+        _xrays_reset()
+
+    #resetting mono and other beamline stuff
+    if iex.BL.xrays:
+        _xrays_reset()
+
+
+##############################################################################################################
+##############################                    get all                 ##############################
+##############################################################################################################
+def Octupole_get_all(verbose=True):
+    """
+    returns a dictionary with the current status of the Kappa endstation and exit slit
+    """
+    vals = {}
+
+    #sample postion
+    motor_dictionary = _Octupole_motor_dictionary()
+    for motor in motor_dictionary.keys():
+        vals[motor]=Octupole_Motors.get(motor,verbose=False)
+
+    #endstation/branch pvs
+    extra_pvs = Octupole_extra_pvs()
+    for key in extra_pvs.keys():
+        vals.update(key,caget(extra_pvs[key]))
+    if iex.BL.xrays:
+        vals.update('exit_slit',slit3D_get())
+
+    #beamline info
+    if iex.BL.xray:
+        beamline_info = xrays_get_all()
+        vals.update(beamline_info) 
+
+    mesh.get()
+    vals.update({'mesh':mesh.current})
+
+    if verbose:
+        print("-----------------------------------------------------------")   
+        for key in vals:
+            print(key+" = "+vals[key])
+        print("-----------------------------------------------------------")   
+    return vals
+
+##############################################################################################################
+##############################                         logging                  ##############################
+##############################################################################################################
+def _Octupole_log_header():
+    """
+    header for the log file
+    """
+    h = "scan,motor,start,stop,step,x,y,z,th,tth,TA"
+    h += "hv,exit_slit,GRT,ID_SP,ID_RBV,ID_Mode,ID_QP,TEY,mesh,det_name,mpa_HV,m3r_centroid,time,comment"
+    header_list = {'Octupole':h}
+    return header_list 
+
+def _Octupole_log_entries():
+    """
+    enstation info for log file
+
+    Previously: scanlog
+    """
+    vals = Kappa_get_all(verbose=False)
+    x = vals['x']
+    y = vals['y']
+    z = vals['z']
+    th = vals['th']
+    tth = vals['tth']
+  
+
+    TA = vals['TA']
+    mesh.get()
+    tey_current = tey.current
+    mesh_current = mesh.current
+
+    #m3r_centroid = vals['kphi'] ????
+
+    entry_list = ["x","y","z","tth","kth","kap","kphi","TA","TB","TEY","mesh","det_name","mpa_HV","m3r_centroid"]
+    pv_list = [ x, y, z, tth, kth, kap,kphi, TA, TB,tey_current,mesh_current,det_name,mpa_HV,m3r_centroid]  
+    format_list = [".2f",".2f",".2f",".2f",".2f",".2f",".2f",".2f",".2f","1.2e","1.2e","s",".0f",".0f"]
+
+    return entry_list,pv_list,format_list
+
+##############################################################################################################
+##############################             Octupole scanRecord           ##############################
+##############################################################################################################
+def _Octupole_scan_before_sequence(scan_ioc,scan_dim,**kwargs):
+    """
+    writes the user string sequence to happen at the beginning of a scan
+    returns before_scan_pv = pv for userStringSeq for before scan
+
+    **kwargs
+        seq_num: userStringSeq number in ioc => 9 (default)
+
+    Previously: BeforeScan_StrSeq
+    """
+    kwargs.setdefault('seq_num',9)
+    seq_num=kwargs['seq_num']
+
+    before_scan_pv,before_scan_proc = userStringSeq_pvs(scan_ioc, seq_num)
+
+    #clear and write the before scan user sequence
+    userStringSeq_clear(scan_ioc,seq_num)
+    caput(before_scan_pv+".DESC","Before Scan")
+
+    #This is where you'd do something if need (CA -> 'Passive', etc)
+    pv=''
+    cmd=''
+    caput(before_scan_pv+".LNK" +str(1),pv)
+    caput(before_scan_pv+".STR" +str(1),cmd)
+
+    return before_scan_proc
+
+def _Octupole_scan_after_sequence(scan_ioc,scan_dim,**kwargs):
+    """
+    writes the user string sequence to happen at the end of a scan
+    returns after_scan_pv = pv for userStringSeq for after scan
+
+    **kwargs
+        seq_num: userStringSeq number in ioc => 10 (default)
+        snake: for snake scanning => False (default)
+
+    Previously: AfterScan_StrSeq
+    """
+    kwargs.setdefault('seq_num',10)
+    kwargs.setdefault('snake',False)
+    seq_num=kwargs['seq_num']
+
+    if 'scan_ioc' in kwargs:
+        scan_ioc = kwargs['scan_ioc']
+        try:
+            iex.BL.ioc = kwargs['scan_ioc']
+        except:
+            error = 'undefined'
+    else:
+        scan_ioc = iex.BL.ioc
+
+    after_scan_pv,after_scan_proc = userStringSeq_pvs(scan_ioc, seq_num)
+    
+    #clear and write the after scan user sequence
+    userStringSeq_clear(scan_ioc,seq_num)
+    caput(after_scan_pv+".DESC","After Scan")
+        
+    scan_pv = iex.BL.ioc+"scan"+str(scan_dim)
+    ## Put scan record back in absolute mode
+    caput(after_scan_pv+".LNK2",scan_pv+".P1AR")
+    caput(after_scan_pv+".STR2","ABSOLUTE")
+
+    ## Put Positionner Settling time to 0.1s
+    caput(after_scan_pv+".LNK3",scan_pv+".PDLY NPP NMS")
+    caput(after_scan_pv+".DO3",0.1)
+
+    ## Clear DetTriggers 2 to 4:
+    caput(after_scan_pv+".LNK4",scan_pv+".T2PV NPP NMS")    #remove trigger 2 (used for EA) after scan
+
+    if kwargs['snake']:
+        snake_dim = scan_dim - 1
+        #snake_proc= Kappa_snake_pv(snake_dim,enable=True)
+        #caput(after_scan_pv+".LNK10",Kappa_snake_pv()+"PP NMS")
+        #caput(after_scan_pv+".D10",1)
+
+    return after_scan_proc
+
+def _Octupole_detector_triggers_sequence(scan_ioc,scan_dim,**kwargs):    # do we need to add 29idb:ca5 ???
+    """
+    
+    """
+
+    kwargs.setdefault(seq_num,8)
+    seq_num=kwargs['seq_num']
+
+    detector_triggers_pv,detector_triggers_proc = userStringSeq_pvs(iex.BL.ioc, seq_num)
+       
+    #clear the userStringSeq
+    userStringSeq_clear(scan_ioc,seq_num=kwargs['seq_num'])
+    caput(detector_triggers_pv+".DESC","Octupole_Trigger1")
+
+    scaler_pv = Octupole_scaler_pv()
+
+    caput(detector_triggers_pv+".LNK" +str(1),scaler_pv)
+    caput(detector_triggers_pv+".WAIT"+str(1),"After"+str(last))
+    
+    ca_list = _Octupole_detector_list()
+    last = len(ca_list)
+    for i,ca in enumerate(ca_list):
+        ca_pv = Keithley_pv(ca[0], ca[1])+':read.PROC CA NMS'
+        caput(detector_triggers_pv+".LNK" +str(i+2),ca_pv)
+        caput(detector_triggers_pv+".WAIT"+str(i+2),"After"+str(last))
+
+    return detector_triggers_proc
+
+def _Octupole_trigger_dictionary(scan_ioc,scan_dim,**kwargs):
+    """
+    need to do something
+    """
+    trigger_dictionary = {
+        1:_Octupole_detector_triggers_sequence(scan_ioc,scan_dim,**kwargs),
+    }
+    return trigger_dictionary
+
+
+
+
+##############################################################################################################
+##############################        Octupole  Motor Scan Set Up        ##############################
+##############################################################################################################
+
+
+
+
+
+##############################################################################################################
+##############################             Octupole light       ##############################
+############################################################################################################## 
+def Octupole_light(ON_OFF):
+    """
+    Previously:light
+    """
+    light_pv = '29ide:Unidig1Bo0'
+    if ON_OFF.lower() == 'on':
+        light=0
+    elif ON_OFF.lower() == 'off':
+        light=1
+    caput(light_pv,light)
+    print(("Turning light "+ON_OFF+"."))
+
+
+
+
+
+##############################################################################################################
+##############################             Octupole safestate        ##############################
+##############################################################################################################
+def Octupole_safe_state(**kwargs):
+    """
+    puts the C-branch in a safe state, 
+    **kwargs
+        shutter_close = True; closes the D-shutter
+        valve_close = True; closes the D-valve
+    """
+    kwargs.setdefault("shutter_close",True)
+    kwargs.setdefault("valve_close",True)
+
+    if kwargs['shutter_close']:
+        branch_shutter_close()
+    
+    if kwargs['valve_close']:
+       branch_valve_close()
+
+
+    
\ No newline at end of file
diff --git a/iexcode/instruments/scalers.py b/iexcode/instruments/scalers.py
index c3cef3b..7face9f 100644
--- a/iexcode/instruments/scalers.py
+++ b/iexcode/instruments/scalers.py
@@ -30,6 +30,20 @@ def Kappa_scaler(time_seconds=0.1,verbose=True):
     if verbose: 
         print("Integration time set to:", str(time_seconds))
 
+
+def Octupole_scaler(time_seconds=0.1,verbose=True):
+    """
+    Sets the integration time for the scalers in the Kappa chamber
+    and mpa filter num if mpa is running
+    """
+    scaler_pv="29ideMZ0:scaler1.TP"
+    
+    caput(pv,time_seconds)
+
+    if verbose: 
+        print("Integration time set to:", str(time_seconds))
+
+
 class Scaler:
     def __inti__(self, pv):
         """
-- 
GitLab