From ef55df4205b59dc134512d0a75c4892cf7fae88c Mon Sep 17 00:00:00 2001
From: "FR@29iduser" <rodolakis@anl.gov>
Date: Fri, 30 Sep 2022 17:06:58 -0500
Subject: [PATCH] snake scan implemented in ARPES

---
 build/lib/iexcode/instruments/ARPES.py      | 134 +++++++++++++++-----
 build/lib/iexcode/instruments/Motors.py     |   2 +-
 build/lib/iexcode/instruments/scanRecord.py |  22 +++-
 build/lib/iexcode/instruments/userCalcs.py  |  39 +++++-
 iexcode/instruments/ARPES.py                | 131 ++++++++++++++-----
 iexcode/instruments/Motors.py               |   2 +-
 iexcode/instruments/scanRecord.py           |  22 +++-
 iexcode/instruments/userCalcs.py            |  39 +++++-
 8 files changed, 303 insertions(+), 88 deletions(-)

diff --git a/build/lib/iexcode/instruments/ARPES.py b/build/lib/iexcode/instruments/ARPES.py
index eb59983..3b3c5af 100644
--- a/build/lib/iexcode/instruments/ARPES.py
+++ b/build/lib/iexcode/instruments/ARPES.py
@@ -15,7 +15,7 @@ from iexcode.instruments.xrays import _xrays_detector_dictionary, _xrays_reset,
 
 from iexcode.instruments.conversions_constants import *
 from iexcode.instruments.utilities import *
-from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs
+from iexcode.instruments.userCalcs import *
 
 from iexcode.instruments.Motors import Motors
 from iexcode.instruments.current_amplifiers import Keithley, _Keithley_pv, ca_reset_all, _ca_live_sequence
@@ -99,6 +99,9 @@ def ARPES_init(*userName,**kwargs):
     tey = Keithley('c',1)
     ca15 = Keithley('b',15)
   
+    #default scan settings
+    ARPES_default_scan_setting()
+
     #resetting everything
     if kwargs['reset']:
         ARPES_reset()
@@ -228,19 +231,29 @@ def folders_ARPES(user_name,**kwargs):
         else:
             print_warning_message("EA ioc is not running, h5 folder not set")
 
-def ARPES_reset():
+def ARPES_default_scan_setting():
+    """
+    default setting for scan record
+    Note: only get set if ARPES_reset is run 
     """
-    resets scanRecord, current amplifiers, mono limits and lakeshore
-    """   
     #writing default parameters to iex.BL.mda
+    
+    iex.BL.mda.detector_dictionary = _ARPES_detector_dictionary()
     if iex.BL.mode=='staff':
-        iex.BL.mda.detector_dictionary = staff_detector_dictionary()
-    else:
-        iex.BL.mda.detector_dictionary = _ARPES_detector_dictionary()
-    iex.BL.mda.trigger_dictionary = _ARPES_trigger_dictionary()
-    iex.BL.mda.scan_before_sequence = _ARPES_scan_before_sequence()
-    iex.BL.mda.scan_after_sequence = _ARPES_scan_after_sequence()
+        iex.BL.mda.detector_dictionary.update(staff_detector_dictionary())
     
+    iex.BL.mda.trigger_dictionary = _ARPES_trigger_dictionary()
+    iex.BL.mda.before_scan_pv = _ARPES_scan_before_sequence()
+    iex.BL.mda.after_scan_pv = _ARPES_scan_after_sequence()
+    iex.BL.mda.snake_set = ARPES_snake_set #sending the function, no parenthesis
+
+    #set the logfile name
+    iex.BL.mda.log.name_set()
+
+def ARPES_reset():
+    """
+    resets scanRecord, current amplifiers, mono limits and lakeshore
+    """       
     #resetting the scanRecord
     print("resetting the scanRecord - "+iex.BL.ioc)
     iex.BL.mda.reset_all()
@@ -267,8 +280,7 @@ def ARPES_reset():
     print("resetting ARPES Lakeshore")
     Lakeshore_reset(pv,LS_355_defaults)
 
-    #set the logfile name
-    iex.BL.mda.log.name_set()
+
 
 ##############################################################################################################
 ##############################                    get all                 ##############################
@@ -373,7 +385,7 @@ def _ARPES_scan_before_sequence(**kwargs):
     #sequence put CAs in passive
     ca_list = _ARPES_ca_list()
     for (i,ca) in enumerate(ca_list):
-        ca_pv = _Keithley_pv(ca[0], ca[1])+':read.SCAN PP NMS'
+        ca_pv = _Keithley_pv(ca[0], ca[1])+'read.SCAN PP NMS'
         caput(before_scan_pv+".LNK" +str(i+1),ca_pv)
         caput(before_scan_pv+".STR" +str(i+1),"Passive")
 
@@ -387,6 +399,22 @@ def _ARPES_ca_live_sequence(**kwargs):
     ca_live_sequence_proc = _ca_live_sequence(iex.BL.ioc,kwargs['seq_num'],_ARPES_ca_list())
     return ca_live_sequence_proc
 
+def _ARPES_scan_after_pvs(clear=False,**kwargs):
+    """
+    _ARPES_scan_after_sequence pv
+
+    """
+    kwargs.setdefault('seq_num',10)
+    seq_num = kwargs['seq_num']
+
+    scan_ioc = iex.BL.ioc
+
+    after_scan_pv,after_scan_proc = userStringSeq_pvs(scan_ioc, seq_num)
+
+    if clear:
+        userStringSeq_clear(scan_ioc,seq_num)
+    return after_scan_pv,after_scan_proc
+
 def _ARPES_scan_after_sequence(**kwargs):
     """
     writes the user string sequence to happen at the end of a scan
@@ -397,24 +425,18 @@ def _ARPES_scan_after_sequence(**kwargs):
         snake: for snake scanning => False (default)
     Previously: AfterScan_StrSeq
     """
-    kwargs.setdefault('seq_num',10)
-    kwargs.setdefault('snake',False)
+    
     kwargs.setdefault('scan_dim',1)
-
-    seq_num = kwargs['seq_num']
     scan_dim = kwargs['scan_dim']
 
-    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)
+    after_scan_pv,after_scan_proc = _ARPES_scan_after_pvs(clear=True,**kwargs)
+      
     caput(after_scan_pv+".DESC","After Scan")
         
     ## Put All relevant CA back in live mode
     ca_live_sequence_proc = _ARPES_ca_live_sequence(**kwargs)
-    caput(after_scan_pv+".LNK1",ca_live_sequence_proc+" PP NMS")
+    caput(after_scan_pv+".LNK1",ca_live_sequence_proc+" NP NMS")
     caput(after_scan_pv+".DO1",1)
 
     scan_pv = iex.BL.ioc+"scan"+str(scan_dim)
@@ -428,13 +450,10 @@ def _ARPES_scan_after_sequence(**kwargs):
 
     ## Clear DetTriggers 2:
     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= ARPES_snake_pv(snake_dim,enable=True)
-        #caput(after_scan_pv+".LNK10",ARPES_snake_pv()+"PP NMS")
-        #caput(after_scan_pv+".D10",1)
-
+    caput(after_scan_pv+".STR4","")
+    
+    ##Snake goes in LNKA (10)
+ 
     return after_scan_proc
 
 
@@ -448,7 +467,7 @@ def _ARPES_detector_triggers_sequence(**kwargs):
     detector_triggers_pv,detector_triggers_proc = userStringSeq_pvs(scan_ioc, seq_num)
        
     #clear the userStringSeq
-    userStringSeq_clear(scan_ioc,seq_num=kwargs['seq_num'])
+    userStringSeq_clear(scan_ioc, seq_num)
     caput(detector_triggers_pv+".DESC","ARPES_Trigger1")
     
     ca_list = _ARPES_ca_list()
@@ -469,6 +488,48 @@ def _ARPES_trigger_dictionary(**kwargs):
     }
     return trigger_dictionary
 
+def _ARPES_snake_userCalc(**kwargs):
+    """
+    used to flip the sign of the step after a scan 
+        snake scans as opposed to typewriter scans
+    """
+    kwargs.setdefault('seq_num',10)
+    kwargs.setdefault('scan_dim',1)
+
+    seq_num=kwargs['seq_num']
+    scan_ioc = iex.BL.ioc
+    
+    userCalc_clear(scan_ioc,seq_num)
+    snake_userCalc_pv,snake_userCalc_proc = userCalcs_pvs(scan_ioc, seq_num)
+
+    scan_step_pv = scan_ioc+'scan'+str(kwargs['scan_dim'])+'.P1SI NP NMS'
+
+    caput(snake_userCalc_pv+'.DESC','snake_calc'),
+    caput(snake_userCalc_pv+'.SCAN','I/O Intr'),
+    caput(snake_userCalc_pv+'.INAN',scan_step_pv)
+    caput(snake_userCalc_pv+'.B',"-1")
+    caput(snake_userCalc_pv+'.CALC$',"A*B")
+    
+    return snake_userCalc_pv+'.VAL'
+
+
+def ARPES_snake_set(snake,**kwargs):
+    """
+    used for snake scanning 
+    """
+    kwargs.setdefault('scan_dim',1)
+    scan_ioc = iex.BL.mda.ioc
+    after_scan_pv,after_scan_proc = _ARPES_scan_after_pvs()
+
+    snake_val = _ARPES_snake_userCalc()
+    scan_step_pv = scan_ioc+'scan'+str(kwargs['scan_dim'])+'.P1SI NP NMS'
+
+    if snake:
+        caput(after_scan_pv+".LNKA",scan_step_pv)
+        caput(after_scan_pv+"DOA",snake_val)
+    else:
+        caput(after_scan_pv+".LNKA","")
+        caput(after_scan_pv+"DOA",0)
 
 ##############################################################################################################
 ##############################             ARPES Motor Endcoders      ##############################
@@ -725,12 +786,17 @@ def scanfocus(x_start,x_stop,x_step,**kwargs):
 
    
 
-def ARPES_sample_map2D(step_y=0.5,step_z=0.5):
+def ARPES_map_sample(y_start=-2,y_stop=2,y_step=0.5,z_start=12,z_stop=16,z_step=0.5,**kwargs):
     """
     2D map of sample area in ARPES chamber
 
+    *kwargs
+        snake: True (default); otherwise a typewriter scan
+        comment: to add a comment to the end of the meta data in the logfile
+        relative: True to scan relatively from current position
+
     Previously: Map_ARPES_Sample
     """
-    print("Scan_ARPES_2Dmotor(\"y\",0,4,"+str(step_y)+",\"z\",12,16,"+str(step_z)+")")
-    ARPES_Motors.scan_2D(["y",0,4,step_y],["z",12,16,step_z])
+    kwargs.setdefault('snake',True)
+    ARPES_Motors.scan_2D(["y",y_start,y_stop,y_step],["z",z_start,z_stop,z_step],**kwargs)
 
diff --git a/build/lib/iexcode/instruments/Motors.py b/build/lib/iexcode/instruments/Motors.py
index 0e216a2..475e362 100644
--- a/build/lib/iexcode/instruments/Motors.py
+++ b/build/lib/iexcode/instruments/Motors.py
@@ -201,7 +201,7 @@ class Motors:
             relative: scab => True/False; default = False (absolute)
             outer_scan_dim: 2  (default)
             execute: True/False to start the scan => True (default)
-            Snake; coming soon
+            snake; testing
 
 
             for Kappa only:
diff --git a/build/lib/iexcode/instruments/scanRecord.py b/build/lib/iexcode/instruments/scanRecord.py
index 25acc9f..d0537a2 100644
--- a/build/lib/iexcode/instruments/scanRecord.py
+++ b/build/lib/iexcode/instruments/scanRecord.py
@@ -105,11 +105,11 @@ class ScanRecord:
             mda.trigger_dictionary: dictionary corresponding to trigger num and trigger pv => {trigger_num:'trigger_pv'}
             mda.before_scan_pv: pv to be proc'ed be for the start of a scan
             mda.after_scan_pv: pv to be proc'ed be for the start of a scan
+            mda.snake_set: function which adds/removes sign of step to the after_scan
             mda.log
         
         **kwargs:
         if a kwargs is not specified then it will not modify that field of the scanRecord
-
             empty = True
                 kwargs = {
                     'detector_dictionary':'{}',
@@ -117,11 +117,6 @@ class ScanRecord:
                     'before_scan_pv':"",
                     'after_scan_pv':""
                 }
-
-            detector_dictionary
-            trigger_dictionary
-            before_scan_pv
-            after_scan_pv
         """
 
         self.ioc = scan_ioc
@@ -168,6 +163,11 @@ class ScanRecord:
             self.log = kwargs['logging']
         else:
             self.log = None
+
+        if 'snake_set' in kwargs:
+            self.snake_set = kwargs['snake_set']
+        else:
+            self.snake_set = None
         pass
 
 
@@ -765,6 +765,7 @@ class ScanRecord:
         Previously: part of Scan_Go
         """
         kwargs.setdefault('scan_dim',1)
+        kwargs.setdefault('snake',False)
         scan_dim = kwargs['scan_dim']
 
         self.progress(scan_dim)
@@ -781,6 +782,14 @@ class ScanRecord:
                 shutter_check(verbose=True)
         except:
             print_warning_message('no shutter check')
+
+        try:
+            self.snake_set(kwargs['snake'])
+        except:
+            error = 'snake scanning not configured for this BL_config'
+            if verbose:
+                print(error)
+
         if self.check(scan_dim):
             filename = self.prefix()
             fileNum  = self.fileNum()
@@ -792,6 +801,7 @@ class ScanRecord:
             print(filename+str(fileNum)+" finished at ", dateandtime())
             print('\n')
             
+      
 
     ##############################################################################################################
     #############################          empty and time scans               ##############################
diff --git a/build/lib/iexcode/instruments/userCalcs.py b/build/lib/iexcode/instruments/userCalcs.py
index 367ceec..de49f36 100644
--- a/build/lib/iexcode/instruments/userCalcs.py
+++ b/build/lib/iexcode/instruments/userCalcs.py
@@ -39,8 +39,42 @@ def userStringSeq_clear(ioc,seq_num):
     return pv
 
 ##############################################################################################################
-###########################      User CalcsOut         ######################
+###########################      userCalcs         ######################
 ##############################################################################################################
+def userCalcs_pvs(ioc, seq_num):     
+    """
+    returns the proc pv for a given userStringSeq
+    userStringSeq, proc_pv  
+
+    Previously: BeforeScan_StrSeq, AfterScan_StrSeq
+    """
+    userCalc_pv = ioc+"userCalc"+str(seq_num)
+    userCalc_proc = userCalc_pv+".PROC"
+    return userCalc_pv, userCalc_proc
+
+def userCalc_clear(ioc,calcOut_num):
+    """
+    clears the nth userCalc in the specified ioc
+
+    Previously:ClearCalc
+    """
+    pv=ioc+"userCalc"+str(calcOut_num)
+    caput(pv+".DESC","")
+
+    for i in ["A","B","C","D","E","F","G","H","I","J","K","L"]:
+        caput(pv+'.IN'+i+'P',"Yes")
+        caput(pv+'.IN'+i+'N',"")
+        caput(pv+"."+i,0)
+
+    caput(pv+".CALC$","")
+    caput(pv+".DOLN","")
+    caput(pv+".OUTN","")
+    caput(pv+".OOPT","Every Time")
+    return pv    
+##############################################################################################################
+###########################      userCalcsOut         ######################
+##############################################################################################################
+
 def userCalcOut_clear(ioc,calcOut_num):
     """
     clears the nth userCalcOut in the specified ioc
@@ -60,9 +94,8 @@ def userCalcOut_clear(ioc,calcOut_num):
     caput(pv+".OOPT","On Change")
     return pv
 
-
 ##############################################################################################################
-###########################      User Calcs, User  Average        User  Average         ######################
+###########################    User  Average        User  Average         ######################
 ##############################################################################################################
 def userAvg_clear(ioc,userAvg_num):
     """
diff --git a/iexcode/instruments/ARPES.py b/iexcode/instruments/ARPES.py
index eb59983..70b4ea4 100644
--- a/iexcode/instruments/ARPES.py
+++ b/iexcode/instruments/ARPES.py
@@ -15,7 +15,7 @@ from iexcode.instruments.xrays import _xrays_detector_dictionary, _xrays_reset,
 
 from iexcode.instruments.conversions_constants import *
 from iexcode.instruments.utilities import *
-from iexcode.instruments.userCalcs import userStringSeq_clear, userStringSeq_pvs
+from iexcode.instruments.userCalcs import *
 
 from iexcode.instruments.Motors import Motors
 from iexcode.instruments.current_amplifiers import Keithley, _Keithley_pv, ca_reset_all, _ca_live_sequence
@@ -99,6 +99,9 @@ def ARPES_init(*userName,**kwargs):
     tey = Keithley('c',1)
     ca15 = Keithley('b',15)
   
+    #default scan settings
+    ARPES_default_scan_setting()
+
     #resetting everything
     if kwargs['reset']:
         ARPES_reset()
@@ -228,19 +231,29 @@ def folders_ARPES(user_name,**kwargs):
         else:
             print_warning_message("EA ioc is not running, h5 folder not set")
 
-def ARPES_reset():
+def ARPES_default_scan_setting():
+    """
+    default setting for scan record
+    Note: only get set if ARPES_reset is run 
     """
-    resets scanRecord, current amplifiers, mono limits and lakeshore
-    """   
     #writing default parameters to iex.BL.mda
+    
+    iex.BL.mda.detector_dictionary = _ARPES_detector_dictionary()
     if iex.BL.mode=='staff':
-        iex.BL.mda.detector_dictionary = staff_detector_dictionary()
-    else:
-        iex.BL.mda.detector_dictionary = _ARPES_detector_dictionary()
-    iex.BL.mda.trigger_dictionary = _ARPES_trigger_dictionary()
-    iex.BL.mda.scan_before_sequence = _ARPES_scan_before_sequence()
-    iex.BL.mda.scan_after_sequence = _ARPES_scan_after_sequence()
+        iex.BL.mda.detector_dictionary.update(staff_detector_dictionary())
     
+    iex.BL.mda.trigger_dictionary = _ARPES_trigger_dictionary()
+    iex.BL.mda.before_scan_pv = _ARPES_scan_before_sequence()
+    iex.BL.mda.after_scan_pv = _ARPES_scan_after_sequence()
+    iex.BL.mda.snake_set = ARPES_snake_set #sending the function, no parenthesis
+
+    #set the logfile name
+    iex.BL.mda.log.name_set()
+
+def ARPES_reset():
+    """
+    resets scanRecord, current amplifiers, mono limits and lakeshore
+    """       
     #resetting the scanRecord
     print("resetting the scanRecord - "+iex.BL.ioc)
     iex.BL.mda.reset_all()
@@ -267,8 +280,7 @@ def ARPES_reset():
     print("resetting ARPES Lakeshore")
     Lakeshore_reset(pv,LS_355_defaults)
 
-    #set the logfile name
-    iex.BL.mda.log.name_set()
+
 
 ##############################################################################################################
 ##############################                    get all                 ##############################
@@ -373,7 +385,7 @@ def _ARPES_scan_before_sequence(**kwargs):
     #sequence put CAs in passive
     ca_list = _ARPES_ca_list()
     for (i,ca) in enumerate(ca_list):
-        ca_pv = _Keithley_pv(ca[0], ca[1])+':read.SCAN PP NMS'
+        ca_pv = _Keithley_pv(ca[0], ca[1])+'read.SCAN PP NMS'
         caput(before_scan_pv+".LNK" +str(i+1),ca_pv)
         caput(before_scan_pv+".STR" +str(i+1),"Passive")
 
@@ -387,6 +399,22 @@ def _ARPES_ca_live_sequence(**kwargs):
     ca_live_sequence_proc = _ca_live_sequence(iex.BL.ioc,kwargs['seq_num'],_ARPES_ca_list())
     return ca_live_sequence_proc
 
+def _ARPES_scan_after_pvs(clear=False,**kwargs):
+    """
+    _ARPES_scan_after_sequence pv
+
+    """
+    kwargs.setdefault('seq_num',10)
+    seq_num = kwargs['seq_num']
+
+    scan_ioc = iex.BL.ioc
+
+    after_scan_pv,after_scan_proc = userStringSeq_pvs(scan_ioc, seq_num)
+
+    if clear:
+        userStringSeq_clear(scan_ioc,seq_num)
+    return after_scan_pv,after_scan_proc
+
 def _ARPES_scan_after_sequence(**kwargs):
     """
     writes the user string sequence to happen at the end of a scan
@@ -397,24 +425,18 @@ def _ARPES_scan_after_sequence(**kwargs):
         snake: for snake scanning => False (default)
     Previously: AfterScan_StrSeq
     """
-    kwargs.setdefault('seq_num',10)
-    kwargs.setdefault('snake',False)
+    
     kwargs.setdefault('scan_dim',1)
-
-    seq_num = kwargs['seq_num']
     scan_dim = kwargs['scan_dim']
 
-    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)
+    after_scan_pv,after_scan_proc = _ARPES_scan_after_pvs(clear=True,**kwargs)
+      
     caput(after_scan_pv+".DESC","After Scan")
         
     ## Put All relevant CA back in live mode
     ca_live_sequence_proc = _ARPES_ca_live_sequence(**kwargs)
-    caput(after_scan_pv+".LNK1",ca_live_sequence_proc+" PP NMS")
+    caput(after_scan_pv+".LNK1",ca_live_sequence_proc+" NP NMS")
     caput(after_scan_pv+".DO1",1)
 
     scan_pv = iex.BL.ioc+"scan"+str(scan_dim)
@@ -428,13 +450,10 @@ def _ARPES_scan_after_sequence(**kwargs):
 
     ## Clear DetTriggers 2:
     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= ARPES_snake_pv(snake_dim,enable=True)
-        #caput(after_scan_pv+".LNK10",ARPES_snake_pv()+"PP NMS")
-        #caput(after_scan_pv+".D10",1)
-
+    caput(after_scan_pv+".STR4","")
+    
+    ##Snake goes in LNKA (10)
+ 
     return after_scan_proc
 
 
@@ -448,7 +467,7 @@ def _ARPES_detector_triggers_sequence(**kwargs):
     detector_triggers_pv,detector_triggers_proc = userStringSeq_pvs(scan_ioc, seq_num)
        
     #clear the userStringSeq
-    userStringSeq_clear(scan_ioc,seq_num=kwargs['seq_num'])
+    userStringSeq_clear(scan_ioc, seq_num)
     caput(detector_triggers_pv+".DESC","ARPES_Trigger1")
     
     ca_list = _ARPES_ca_list()
@@ -469,7 +488,46 @@ def _ARPES_trigger_dictionary(**kwargs):
     }
     return trigger_dictionary
 
+def _ARPES_snake_userCalc(**kwargs):
+    """
+    used to flip the sign of the step after a scan 
+        snake scans as opposed to typewriter scans
+    """
+    kwargs.setdefault('seq_num',10)
+    kwargs.setdefault('scan_dim',1)
 
+    seq_num=kwargs['seq_num']
+    scan_ioc = iex.BL.ioc
+    
+    userCalc_clear(scan_ioc,seq_num)
+    snake_userCalc_pv,snake_userCalc_proc = userCalcs_pvs(scan_ioc, seq_num)
+
+    scan_step_pv = scan_ioc+'scan'+str(kwargs['scan_dim'])+'.P1SI NP NMS'
+
+    caput(snake_userCalc_pv+'.DESC','snake_calc'),
+    caput(snake_userCalc_pv+'.SCAN','I/O Intr'),
+    caput(snake_userCalc_pv+'.INAN',scan_step_pv)
+    caput(snake_userCalc_pv+'.B',"-1")
+    caput(snake_userCalc_pv+'.CALC$',"A*B")
+    
+    return snake_userCalc_pv+'.VAL'
+
+def ARPES_snake_set(snake,**kwargs):
+    """
+    used for snake scanning 
+    """
+    kwargs.setdefault('scan_dim',1)
+    scan_ioc = iex.BL.mda.ioc
+    after_scan_pv,after_scan_proc = _ARPES_scan_after_pvs()
+
+    scan_step_pv = scan_ioc+'scan'+str(kwargs['scan_dim'])+'.P1SI NP NMS'
+
+    if snake:
+        caput(after_scan_pv+".LNKA",scan_step_pv)
+        caput(after_scan_pv+".DOLA",_ARPES_snake_userCalc()+" NP")
+    else:
+        caput(after_scan_pv+".LNKA","")
+        caput(after_scan_pv+".DOA","")
 ##############################################################################################################
 ##############################             ARPES Motor Endcoders      ##############################
 ##############################################################################################################
@@ -725,12 +783,17 @@ def scanfocus(x_start,x_stop,x_step,**kwargs):
 
    
 
-def ARPES_sample_map2D(step_y=0.5,step_z=0.5):
+def ARPES_map_sample(y_start=-2,y_stop=2,y_step=0.5,z_start=12,z_stop=16,z_step=0.5,**kwargs):
     """
     2D map of sample area in ARPES chamber
 
+    *kwargs
+        snake: True (default); otherwise a typewriter scan
+        comment: to add a comment to the end of the meta data in the logfile
+        relative: True to scan relatively from current position
+
     Previously: Map_ARPES_Sample
     """
-    print("Scan_ARPES_2Dmotor(\"y\",0,4,"+str(step_y)+",\"z\",12,16,"+str(step_z)+")")
-    ARPES_Motors.scan_2D(["y",0,4,step_y],["z",12,16,step_z])
+    kwargs.setdefault('snake',True)
+    ARPES_Motors.scan_2D(["y",y_start,y_stop,y_step],["z",z_start,z_stop,z_step],**kwargs)
 
diff --git a/iexcode/instruments/Motors.py b/iexcode/instruments/Motors.py
index 0e216a2..475e362 100644
--- a/iexcode/instruments/Motors.py
+++ b/iexcode/instruments/Motors.py
@@ -201,7 +201,7 @@ class Motors:
             relative: scab => True/False; default = False (absolute)
             outer_scan_dim: 2  (default)
             execute: True/False to start the scan => True (default)
-            Snake; coming soon
+            snake; testing
 
 
             for Kappa only:
diff --git a/iexcode/instruments/scanRecord.py b/iexcode/instruments/scanRecord.py
index 25acc9f..d0537a2 100644
--- a/iexcode/instruments/scanRecord.py
+++ b/iexcode/instruments/scanRecord.py
@@ -105,11 +105,11 @@ class ScanRecord:
             mda.trigger_dictionary: dictionary corresponding to trigger num and trigger pv => {trigger_num:'trigger_pv'}
             mda.before_scan_pv: pv to be proc'ed be for the start of a scan
             mda.after_scan_pv: pv to be proc'ed be for the start of a scan
+            mda.snake_set: function which adds/removes sign of step to the after_scan
             mda.log
         
         **kwargs:
         if a kwargs is not specified then it will not modify that field of the scanRecord
-
             empty = True
                 kwargs = {
                     'detector_dictionary':'{}',
@@ -117,11 +117,6 @@ class ScanRecord:
                     'before_scan_pv':"",
                     'after_scan_pv':""
                 }
-
-            detector_dictionary
-            trigger_dictionary
-            before_scan_pv
-            after_scan_pv
         """
 
         self.ioc = scan_ioc
@@ -168,6 +163,11 @@ class ScanRecord:
             self.log = kwargs['logging']
         else:
             self.log = None
+
+        if 'snake_set' in kwargs:
+            self.snake_set = kwargs['snake_set']
+        else:
+            self.snake_set = None
         pass
 
 
@@ -765,6 +765,7 @@ class ScanRecord:
         Previously: part of Scan_Go
         """
         kwargs.setdefault('scan_dim',1)
+        kwargs.setdefault('snake',False)
         scan_dim = kwargs['scan_dim']
 
         self.progress(scan_dim)
@@ -781,6 +782,14 @@ class ScanRecord:
                 shutter_check(verbose=True)
         except:
             print_warning_message('no shutter check')
+
+        try:
+            self.snake_set(kwargs['snake'])
+        except:
+            error = 'snake scanning not configured for this BL_config'
+            if verbose:
+                print(error)
+
         if self.check(scan_dim):
             filename = self.prefix()
             fileNum  = self.fileNum()
@@ -792,6 +801,7 @@ class ScanRecord:
             print(filename+str(fileNum)+" finished at ", dateandtime())
             print('\n')
             
+      
 
     ##############################################################################################################
     #############################          empty and time scans               ##############################
diff --git a/iexcode/instruments/userCalcs.py b/iexcode/instruments/userCalcs.py
index 367ceec..de49f36 100644
--- a/iexcode/instruments/userCalcs.py
+++ b/iexcode/instruments/userCalcs.py
@@ -39,8 +39,42 @@ def userStringSeq_clear(ioc,seq_num):
     return pv
 
 ##############################################################################################################
-###########################      User CalcsOut         ######################
+###########################      userCalcs         ######################
 ##############################################################################################################
+def userCalcs_pvs(ioc, seq_num):     
+    """
+    returns the proc pv for a given userStringSeq
+    userStringSeq, proc_pv  
+
+    Previously: BeforeScan_StrSeq, AfterScan_StrSeq
+    """
+    userCalc_pv = ioc+"userCalc"+str(seq_num)
+    userCalc_proc = userCalc_pv+".PROC"
+    return userCalc_pv, userCalc_proc
+
+def userCalc_clear(ioc,calcOut_num):
+    """
+    clears the nth userCalc in the specified ioc
+
+    Previously:ClearCalc
+    """
+    pv=ioc+"userCalc"+str(calcOut_num)
+    caput(pv+".DESC","")
+
+    for i in ["A","B","C","D","E","F","G","H","I","J","K","L"]:
+        caput(pv+'.IN'+i+'P',"Yes")
+        caput(pv+'.IN'+i+'N',"")
+        caput(pv+"."+i,0)
+
+    caput(pv+".CALC$","")
+    caput(pv+".DOLN","")
+    caput(pv+".OUTN","")
+    caput(pv+".OOPT","Every Time")
+    return pv    
+##############################################################################################################
+###########################      userCalcsOut         ######################
+##############################################################################################################
+
 def userCalcOut_clear(ioc,calcOut_num):
     """
     clears the nth userCalcOut in the specified ioc
@@ -60,9 +94,8 @@ def userCalcOut_clear(ioc,calcOut_num):
     caput(pv+".OOPT","On Change")
     return pv
 
-
 ##############################################################################################################
-###########################      User Calcs, User  Average        User  Average         ######################
+###########################    User  Average        User  Average         ######################
 ##############################################################################################################
 def userAvg_clear(ioc,userAvg_num):
     """
-- 
GitLab