esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

#%TITLE% mxcollect.mac
#%NAME% %B%mxcollect.mac%B% - macro set for MX datacollection
#%END%

global datacollection_parameters
global SAFETY_SHUTTER_DEVICE
global CURRENT_IMAGE
global STOP_COLLECT_LOOP
global eprodc_log_message
global eprodc_fatal_collect[]
global eprodc_energy_scan_msg
global OLD_COLLECT_SEQ[]
global _ecollect_retDict[]
global COLLECT_SEQ[]
global CURRENT_FRAME_NUMBER

if (whatis("local_skip_images") == 0)
eval("def local_skip_images() \'{}\'")

### Convert the dictionary from python into COLLECT_SEQ
def build_collect_seq() '{
  global datacollection_parameters[]
  unglobal COLLECT_SEQ
  global COLLECT_SEQ[]
  local sequence_pars[]
  local sequence
         
  sequence = datacollection_parameters["oscillation_sequence"]
  if (EPRODC_DEBUG==1)
    print datacollection_parameters
 	
  sequence = sprintf("[%s]", substr(sequence, 3, length(sequence)-4))
  if (EPRODC_DEBUG==1)
    print sequence
  COLLECT_SEQ=eval(sequence)

  for (el in datacollection_parameters) {
    if (el != "oscillation_sequence") {
      COLLECT_SEQ[el]=datacollection_parameters[el]
    }
  }

  if (!COLLECT_SEQ["detector_mode"]) {
    inp[0] = "detector"; inp[1] = "binning"
    COLLECT_SEQ["detector_mode"] = getMxSpecPars(inp)
 }
}'

def datacollection() '{
  egui_fatal("Could not get control of the minidiff")
  SCMinidiffGetControl
  if (SCMinidiffCanMove()!=1) {
    exit
  }
  unglobal eprodc_fatal_collect
  global eprodc_fatal_collect[]

  build_collect_seq()
  
  ecollect()
  
  return _ecollect_retDict
}'

def eprodc_set_mono() '{
  local requested_egy

  if (whatis("COLLECT_SEQ[\"energy\"]")>>16&0x8000) {
    requested_egy = COLLECT_SEQ["energy"]
  } else {
    if (whatis("COLLECT_SEQ[\"wavelength\"]")>>16&0x8000) {
      requested_egy = hc_over_e/COLLECT_SEQ["wavelength"]
    }
  }

  if (requested_egy == 0) {
    # no energy is specified, just ignore
    if (EPRODC_DEBUG==1) print "No energy specified from mxCuBE, ignoring"
    return 1
  }

  if (fabs(getMxEnergy() - requested_egy)<0.002) {
    if (EPRODC_DEBUG == 1) print "Energy is already set properly"
    return 1
  }

  if (EPRODC_DEBUG == 1) print "Requested energy is " requested_egy

  check_beam()

  prodc_set_mono requested_egy

  return 1
}'

def eprodc_transmission() '{
  if (whatis("COLLECT_SEQ[\"transmission\"]")>>16&0x8000) {
    if (EPRODC_DEBUG == 1) print "Setting TRANSMISSION to " COLLECT_SEQ["transmission"]
    transmission COLLECT_SEQ["transmission"]
    # need to add a check here to see if we did it
  }
  return 1
}'

def eprodc_moveDetector() '{
  local detmot,distance

  distance=-1
  if (whatis("COLLECT_SEQ[\"resolution\"][\"upper\"]")>>16&0x8000) {
    if (EPRODC_DEBUG == 1) print "Move detector to resolution " COLLECT_SEQ["resolution"]["upper"]
    get_angles
    distance = CalculateDistanceFromResolution(COLLECT_SEQ["resolution"]["upper"])
  } else if (whatis("COLLECT_SEQ[\"detdistance\"]")>>16&0x8000) {
    if (EPRODC_DEBUG == 1) print "Move detector to " COLLECT_SEQ["detdistance"]
    distance=COLLECT_SEQ["detdistance"]
  }

  if (distance!=-1) {
    detmot = motor_num(getMxSpecPars("detdistmot"))
    _bad_lim =0
    backlash = motor_par(detmot,"backlash")/motor_par(detmot,"step_size")
    _chk_lim detmot distance-backlash
    if (_bad_lim>0) {
      datacollection_cleanup
      return -1
    }
    A[detmot] = distance
    move_em
    waitmove
    return distance
  } else {
    # we do not have an input resolution so no problem
    return 1  
  }
}'

# takes one argument, collectmode- which determines if to skip
# already existing images or not
#
def ecollect() '{
  global STOP_COLLECT_LOOP COLLECT_SEQ
  global CURRENT_IMAGE_FILENAME CURRENT_IMAGE_NUMBER CURRENT_IMAGE_INTENSITY
  global MXSPEC_PARS
  unglobal CURRENT_IMAGE
  global CURRENT_IMAGE

  unglobal _ecollect_retDict
  global  _ecollect_retDict[]
  
  local osc_start osc_range osc_overlap npass \
	exptime nframes comment
  
  cdef("cleanup_once","datacollection_cleanup;","_collect")

  # The STOP_COLLECT_LOOP variable will be set externally from the GUI via specserver
  STOP_COLLECT_LOOP=0

  if (eprodc_transmission() == -1)      {  
    egui_fatal("Could not achieve the required transmission")
    exit
  }
  
  egui_logmsg("Set transmission")
  
  if (eprodc_set_mono() == -1 )         { 
    egui_fatal("Could not achieve the required energy")
    exit
  }
 
  egui_logmsg("Set monochromator to right energy")
  
  if (eprodc_moveDetector() == -1) { 
    egui_fatal("Could not achieve required resolution")
    exit
  }

  egui_logmsg("Set detector to right distance")
  
  #
  # perhaps this should be moved down to after the first ccdprep
  # this would keep the shutter closed while darks are taken
  #
  prodc_prepare_beamline
  egui_logmsg("Beamline prepared")
  
  get_angles
  _ecollect_retDict["curr_detdistance"]  = getMxCurrentDistance()
  _ecollect_retDict["curr_wavelength"]   = getMxWavelength()
  _ecollect_retDict["curr_resolution"]   = CalculateResolutionFromDistance()
  _ecollect_retDict["curr_transmission"] = getMxCurrentTransmission()
  _ecollect_retDict["xbeam"]             = getMxBeamCentreX()
  _ecollect_retDict["ybeam"]             = getMxBeamCentreY() 
  _ecollect_retDict["undulatorGap0"]     = getMxUndulatorGap(1) 
  _ecollect_retDict["undulatorGap1"]     = getMxUndulatorGap(2) 
  _ecollect_retDict["undulatorGap2"]     = getMxUndulatorGap(3) 
  _ecollect_retDict["undulatorType0"]    = getMxUndulatorType(1)
  _ecollect_retDict["undulatorType1"]    = getMxUndulatorType(2)
  _ecollect_retDict["undulatorType2"]    = getMxUndulatorType(3)
  _ecollect_retDict["slitGapHorizontal"] = getMxSlitGapHorizontal()
  _ecollect_retDict["slitGapVertical"]   = getMxSlitGapVertical()
  _ecollect_retDict["beamShape"]         = getMxBeamShape() 
  _ecollect_retDict["beamSizeAtSampleX"] = getMxBeamSizeX() 
  _ecollect_retDict["beamSizeAtSampleY"] = getMxBeamSizeY() 
  _ecollect_retDict["resolutionAtCorner"] = getMxResolutionAtCorner() 
  _ecollect_retDict["resolutionAtHalfCorner"] = getMxResolutionAtHalfCorner() 

  local_skip_images()

  osc_start     = COLLECT_SEQ["start"]
  osc_range     = COLLECT_SEQ["range"]
  osc_overlap   = COLLECT_SEQ["overlap"]
  npass         = COLLECT_SEQ["number_of_passes"]
  exptime       = COLLECT_SEQ["exposure_time"]
  nframes       = COLLECT_SEQ["number_of_images"]
  comment       = COLLECT_SEQ["comment"]

  getMxTotalParams()

  # for mxCuBE to trigger a dark current
  MXCOLLECT_PARS["TakeDarkFlag"] = COLLECT_SEQ["dark"]
  
  # Make sure the fast shutter is closed, important for some h/w configurations
  msclose

  getangles
  local tmp[]
  split(comment, tmp, ",")
  for (k in tmp) {
    local motmne, pos
    local i, j, s
    s = tmp[k]
    i = index(s, ":")
    if (i > 0) {
      motmne = substr(s, 1, i-1)
      motnum = eval(sprintf("motor_num(%s)", motmne))
      if (motnum < 0) {
        print "invalid motor " motmne
      } else {
        pos = substr(s, i+2)
        pos = pos + 0
        printf("Preparing to move motor %s to %f\n", motmne, pos)
        A[motnum] = pos 
      }
    } 
  } 
  print "Waiting for motors to move"
  move_em; waitmove

  write_dp_inputs(COLLECT_SEQ,MXBCM_PARS)

  # D.S 20100304
  # This is a corruption of this macro which should not have equipment specific commands embedded in it
  #
  if (motor_par(phi, "device_id")!="udiff_mot") { musstPX_loadprog }

  # set up the detector in the desired mode (set by the GUI)
  # and program the header parameters

  if ( COLLECT_SEQ["detector_mode"] != 3  ) {
    ccdprep(MXCOLLECT_PARS["TakeDarkFlag"],osc_start,osc_range,exptime,npass,comment)
    egui_logmsg("Detector prepared")
  }

  shopen
  egui_logmsg("Safety shutter open")
  # adjust the gain for the integrated counts
  adjust_i0_i1_gain()

  # initialisation for the first frame
  local angles[] wedgesize first_run_number inverse_run_number frame
  initial_start_angle = osc_start
  initial_stop_angle = initial_start_angle + osc_range 

  angles[0] = initial_start_angle
  angles[1] = initial_stop_angle
  if (fabs(angles[0] - angles[1]) > 0.0001) {
    osc_prepare initial_start_angle initial_stop_angle exptime npass
  } else {
    print "moving phi to " initial_start_angle "for still image"
    osc_goto(initial_start_angle); waitmove
  }
  egui_logmsg("First oscillation prepared")

  if (index(COLLECT_SEQ["experiment_type"],"Inverse") > 0) {
    first_run_number = COLLECT_SEQ["fileinfo"]["run_number"]
    inverse_run_number = COLLECT_SEQ["fileinfo"]["run_number"]+1
    wedgesize = COLLECT_SEQ["reference_interval"]
    for (frame=0;frame< nframes;frame++) {
      egui_logmsg("Starting inverse beam")
      for (wedge=1; wedge <= wedgesize; wedge++) {
	      egui_logmsg(sprintf("Wedge %d of %d",wedge,wedgesize))
	      notskipped = collect_frame(angles[0],angles[1],\
		        exptime,npass,\
		        first_run_number,\
		        frame + wedge -1,\
		        nframes)
	      if (wedgesize != wedge ) {
	        angles = prepare_next_start_angle(initial_start_angle,initial_stop_angle,\
					      exptime,npass,osc_overlap,\
					      wedge + frame -1, \
					      nframes)
	      }
	      if (STOP_COLLECT_LOOP == 1) { frame=nframes-1; wedge=wedgesize }
	      if (notskipped == 1) {
	        if ( COLLECT_SEQ["detector_mode"] != 3  ) {
              if (frame == nframes -1) { ccdflush } else { ccdsave}
            }
          wedge = mxcol_checkIntensity(wedge-1)+1
	      }
      }
      angles = prepare_next_start_angle(initial_start_angle+180,initial_stop_angle+180,\
					    exptime,npass,osc_overlap,\
					    (frame-1),\
					    nframes)

      for (wedge=1; wedge <= wedgesize; wedge++) {
	      notskipped = collect_frame(angles[0],angles[1],\
		            exptime,npass,\
		            inverse_run_number,\
		            frame+ wedge -1,\
		            nframes,1)
	      if (wedgesize != wedge ) {
	        angles = prepare_next_start_angle(initial_start_angle+180,initial_stop_angle+180,\
					          exptime,npass,osc_overlap,\
					          wedge + frame -1,\
					          nframes)
	      }
	      if (STOP_COLLECT_LOOP == 1) { frame=nframes-1; wedge=wedgesize }
	      if (notskipped == 1) {
            if ( COLLECT_SEQ["detector_mode"] != 3  ) {
	          if (frame == nframes -1) {ccdflush } else { ccdsave}
            }
          wedge = mxcol_checkIntensity(wedge-1)+1
        }
      }
      frame+=wedgesize-1
      if ((nframes - frame) <= wedgesize ) wedgesize = nframes - frame - 1
      angles = prepare_next_start_angle(initial_start_angle,initial_stop_angle, \
					exptime,npass,osc_overlap,\
					frame,\
					nframes)
    }  
  } else {
    for (frame=0;frame< nframes;frame++) {
      notskipped = collect_frame(angles[0],angles[1],\
		    exptime,npass,\
		    COLLECT_SEQ["fileinfo"]["run_number"],\
		    frame,\
		    nframes)
      if (STOP_COLLECT_LOOP == 1) frame=nframes-1
      if (notskipped == 1) {
          if ( COLLECT_SEQ["detector_mode"] != 3  ) {
	        if (frame == nframes -1) {ccdflush } else { ccdsave}
          }
        frame = mxcol_checkIntensity(frame)
      }
      angles = prepare_next_start_angle(initial_start_angle,initial_stop_angle,\
					exptime,npass,osc_overlap,\
					frame,\
					nframes)
    }  
  }

  COLLECT_SEQ["successful"]=1

  datacollection_cleanup

  _ecollect_retDict["code"]=2005
  _ecollect_retDict["message"]="Data collection completed"

  #print _ecollect_retDict 
  return _ecollect_retDict
}'

def prepare_next_start_angle(initial_start_angle,initial_stop_angle,exptime,npass,overlap,frame,nframes) '{
  local return_angles[] range
  # prepare next oscillation
  if ((nframes > 1) && (frame < nframes - 1)) {
    range = initial_stop_angle - initial_start_angle
    n_start_angle = initial_start_angle+(frame + 1)*range - (frame + 1)*overlap 
    n_stop_angle = n_start_angle + range

    if (STOP_COLLECT_LOOP == 0) 
      if (fabs(range) > 0.0001) {
	osc_prepare n_start_angle n_stop_angle exptime npass
      } else {
	print "moving phi to " n_start_angle "for still image"
	osc_goto(n_start_angle); waitmove
      }
    return_angles["next_initial_start_angle"]=n_start_angle
    return_angles["next_initial_stop_angle"]=n_stop_angle
  } else {
    return_angles["next_initial_start_angle"]=-1
    return_angles["next_initial_stop_angle"]=-1
  }
  return [0:return_angles["next_initial_start_angle"],1:return_angles["next_initial_stop_angle"]]
}'

# dummy macro needed for if the hook is not defined in collect_frame
def specific_collect_frame_hook(a,b,c,d,e,f,g,h,i,j,k,l) '{
}'

def collect_frame(start_angle,stop_angle,exptime,npass,run_number, frame,nframes,inv) '{
    global CURRENT_FRAME_NUMBER
    local collectedFrameFlag current_image_trigger
    setMxCollectPars("current_phi",start_angle)
    # modify the run number according to what was asked for
    COLLECT_SEQ["fileinfo"]["run_number"]=run_number
    imagefile = ccdfile(COLLECT_SEQ,frame)
    setMxCurrentFilename(imagefile)
    _ecollect_retDict["run_number_total"] += run_number

    CURRENT_FRAME_NUMBER = frame + COLLECT_SEQ["start_image_number"]

    if ((COLLECT_SEQ["skip_images"]==0) || (file_info(imagefile)==0)) {
	egui_logmsg(sprintf("Frame %d, %7.3f to %7.3f degrees",frame+COLLECT_SEQ["start_image_number"],start_angle,stop_angle))
	
	# prepare next start angle here if skip was selected using the previous frame counter to give the right angle for this image
	if ((COLLECT_SEQ["skip_images"]== 1)&&(file_info(imagefile)!=0))
	    prepare_next_start_angle(COLLECT_SEQ["start"],COLLECT_SEQ["start"]+COLLECT_SEQ["range"],exptime,npass,overlap,frame-1,nframes)
	# the following waitmove ensures that the detector is not exposing too long
  # while the phi is still moving to the start angle
  waitmove
  if ( COLLECT_SEQ["detector_mode"] != 3  ) {
    ccdstart
  }
	if (fabs(start_angle - stop_angle) < 0.0001) {
	  msopen; sleep(exptime); msclose
	} else {
	  osc_scan start_angle stop_angle exptime npass frame 
	}
  if ( COLLECT_SEQ["detector_mode"] != 3  ) {
  	ccdread
  }
	_ecollect_retDict["total_start_angles"]+= start_angle
	_ecollect_retDict["total_stop_angles"]+= stop_angle
	_ecollect_retDict["total_passes"]+= npass
	_ecollect_retDict["total_exposure"]+= exptime
	_ecollect_retDict["im_collected"]++
	_ecollect_retDict["intensity"] = CURRENT_IMAGE_INTENSITY
	_ecollect_retDict["totalintensity"] += CURRENT_IMAGE_INTENSITY
	_ecollect_retDict["previous_intensity"] = CURRENT_IMAGE["measuredIntensity"]
  collectedFrameFlag=1

	# save image in database
	#current_image_trigger["image_number"] = frame+COLLECT_SEQ["start_image_number"]
	#current_image_trigger["image_file"] = imagefile
	#current_image_trigger["measuredIntensity"] = CURRENT_IMAGE_INTENSITY
	# setting CURRENT_IMAGE will trigger mxCuBE to store the information
	# in the database.
	# CURRENT_IMAGE=current_image_trigger
	delete CURRENT_IMAGE["image_number"]
	CURRENT_IMAGE["image_file"] = imagefile
	CURRENT_IMAGE["measuredIntensity"] = CURRENT_IMAGE_INTENSITY
	CURRENT_IMAGE["image_number"] = frame+COLLECT_SEQ["start_image_number"]
    } else {
      msg=(sprintf("%s already exists, so skipping...",imagefile))
      egui_logmsg(msg) ; print msg
    if ( COLLECT_SEQ["detector_mode"] != 3  ) {
      if (frame == nframes -1) ccdflush
    }
      collectedFrameFlag=0
    }
    if ((whatis("specific_collect_frame_hook")&2)>>1) {
			specific_collect_frame_hook(start_angle,stop_angle,exptime,npass,run_number, frame,nframes,inv,imagefile,CURRENT_IMAGE_INTENSITY)		  
		}
    return(collectedFrameFlag)
}'

def mxcol_checkIntensity(frame) '{
  local checkIntensityRetval
  print "mxcol_checkIntensity frame " frame
  # do something if the intensity has dropped by more than 90 percent
  checkIntensityRetval = 0
  if (I0THRESHOLD != 0) {
    if (_ecollect_retDict["previous_intensity"] > 0.0 || frame ==0) {
      # if there were no counts on the 1st image force a wait
      if (frame ==0 && CURRENT_IMAGE_INTENSITY < I0THRESHOLD) {
        _ecollect_retDict["previous_intensity"]= CURRENT_IMAGE_INTENSITY*10 + 1
      } else if (_ecollect_retDict["previous_intensity"] < 0.1) {
        # we need an initial value for the previous intensity!
        _ecollect_retDict["previous_intensity"]=1.0
      }
      if (CURRENT_IMAGE_INTENSITY/_ecollect_retDict["previous_intensity"] < 0.1) {
        print "Checking the intensity...."
        mxWaitForBeam()
        checkIntensityRetval = 1
      }
    }
    # rewind 2 frames if possible
    if (checkIntensityRetval ==1) {
      print "We decided to wait for Beam!"
      if (frame > 1) {
         frame = frame -2
      } else if (frame == 0) {
         frame = frame -1
      }
    }
  } else {
    print "Wait for beam disabled while I0THRESHOLD=0. To enable this set I0THRESHOLD to the minimum I0 counts required"
  }
  return(frame)
}'

def mxWaitForBeam() '{
  local exitCondition I0Intensity
  exitCondition=0
  I0Intensity=0
  while (I0Intensity < I0THRESHOLD && exitCondition==0) {
    sleep(1)
    ct 0.1
    I0Intensity = S[i0]
    printf("Counts are %6.4e, waiting for > %6.4e\n",I0Intensity,I0THRESHOLD)
    if (I0Intensity > I0THRESHOLD)  exitCondition=1
  }
    
}'

def datacollection_cleanup '{
  egui_logmsg("doing data collection cleanup...")
  printf("collect cleanup initiated\n")

  # The STOP_COLLECT_LOOP variable will be set externally from the GUI via specserver
  unglobal datacollection_parameters
  global datacollection_parameters

  msclose

  if ((COLLECT_SEQ["in_queue"]==0)||(STOP_COLLECT_LOOP==1)) {
    shclose
  } else if ((COLLECT_SEQ["in_queue"]==1) && (COLLECT_SEQ["successful"]==0)) {
    # we should not close the shutter, but something wrong happened with
    # last image... so we close it anyway
    shclose
  }

  prodc_finish_beamline
  phi_cleanup

  # remove the data collection parameters
  OLD_COLLECT_SEQ=COLLECT_SEQ
  unglobal COLLECT_SEQ
  global COLLECT_SEQ[]
  STOP_COLLECT_LOOP=0

}'

def validate_collect_parameters() '{
  global datacollection_parameters
  local result validpars par
  # code values -1=error 0 = warning, 1=ok
  inp[0]=""
  result["code"] = 1
  result["msg"] = ""
  # build the spec array from the python format input given by mxCuBE
  build_collect_seq()
 
  for (par in MXVALIDATE_PARS) {
      split(MXVALIDATE_PARS[par],inp,",")
      minval = inp[0] + 0.0
      maxval = inp[1] + 0.0
      if (COLLECT_SEQ[par] < minval || COLLECT_SEQ[par] > maxval) {
	result["code"] = 0
	result["msg"] = sprintf("%s\nThe value for %s (%6.2f) is outside the normal values (%6.2f to %6.2f)",result["msg"],par,COLLECT_SEQ[par],minval,maxval)
      }
  }  

  split(MXEXTRAVALIDATE_PARS["rotation_speed"],inp,",")
  min = inp[0] + 0.0;max = inp[1]+0.0
  if (COLLECT_SEQ["exposure_time"] > 0.001) {
    phi_speed = COLLECT_SEQ["range"]/ \
	    (COLLECT_SEQ["exposure_time"]/COLLECT_SEQ["number_of_passes"])
  } else {
    phi_speed = 1.0E99
  }
 
  if (HELICAL_OSCIL == 1) {
    result["code"]=0
    result["msg"]="Helical oscillation is ON"
    #if (COLLECT_SEQ["overlap"] != 0) {
    #  result["code"]=-1
    #  result["msg"]= sprintf("%s\nSorry, cannot use overlap with helical oscillation on", result["msg"])
    #}
    if (helical_check() == -1) {
      result["code"]=-1
      result["msg"]= sprintf("%s\n%s", result["msg"],HELICAL_PARS["msg"])
    }
  }
 
  if (COLLECT_SEQ["number_of_images"]>4 && COLLECT_SEQ["overlap"] < -1) {
    result["code"] = 0
    result["msg"] = sprintf("%s\nAre you sure you want to collect %d images with an overlap of %6.1f?",result["msg"],COLLECT_SEQ["number_of_images"],COLLECT_SEQ["overlap"])
  }
  if (COLLECT_SEQ["overlap"] == 1) {
    result["code"] = 0
    result["msg"] = sprintf("%s\nAre you sure you want to collect %d images with an overlap of %6.1f?",result["msg"],COLLECT_SEQ["number_of_images"],COLLECT_SEQ["overlap"])
  }

  inp[0] = "detector"; inp[1] = "binning"
  default_bin_mode = getMxSpecPars(inp)
  if (whatis("COLLECT_SEQ[\"detector_mode\"]")>>16&0x8000) {
      if (default_bin_mode != COLLECT_SEQ["detector_mode"]) {
	result["code"] = 0
	result["msg"] = sprintf("%s\nThe detector binning mode (%d) (0=software binned, 1=unbinned, 2=hardware binned) is not the default (%d).",result["msg"],COLLECT_SEQ["detector_mode"],default_bin_mode)
      }
  }
  if (phi_speed > max) {
    result["code"] = -1
    result["msg"] = sprintf("%s\nThe calculated axis rotation speed (%6.2f degrees/sec) is faster than the acceptable limit for the hardware (%6.2f to %6.2f)",result["msg"],phi_speed,min,max)
  }
  if (phi_speed < min ) {
    result["code"] = 0
    result["msg"] = sprintf("%s\nThe calculated axis rotation speed (%6.2f degrees/sec) is either 0 or too slow. Please proceed if you have requested still images",result["msg"],phi_speed)
  }

  return result
}'

def update_collect_parameters() '{
  local _parDict

  _parDict["curr_detdistance"]  = getMxCurrentDistance()
  _parDict["curr_wavelength"]   = getMxWavelength()
  _parDict["curr_resolution"]   = CalculateResolutionFromDistance()
  _parDict["curr_transmission"] = getMxCurrentTransmission()
  # now obsolete because mxCuBE should only display the default binning mode
  #_parDict["curr_detector_mode"]     = getMxDetectorBinMode()
  _parDict["FileSuffix"]        = getMxDetectorFileSuffix()
  _parDict["xbeam"]             = getMxBeamCentreX()
  _parDict["ybeam"]             = getMxBeamCentreY() 
	inp[0]="detector";inp[1]="type"
  _parDict["detector"]          = getMxBcmPars(inp)
	inp[1]="binning"
  _parDict["detector_mode"]     = getMxSpecPars(inp)
  _parDict["default_number_of_passes"]     = getMxBcmPars("default_number_of_passes")
  _parDict["minimum_exposure_time"] = getMxBcmPars("minimum_exposure_time")
  _parDict["default_exposure_time"] = getMxBcmPars("default_exposure_time")
  _parDict["minimum_phi_oscillation"] = getMxBcmPars("minimum_phi_oscillation")
  _parDict["maximum_phi_speed"] = getMxBcmPars("maximum_phi_speed")
  _parDict["minimum_phi_speed"] = getMxBcmPars("minimum_phi_speed")
  _parDict["maximum_exposure"] = getMxBcmPars("maximum_exposure")
  # also need beamsize vertical,horizontal, fill mode, min/max exposure time/rotation speed, detector type
  return _parDict
}'

# make the gain adjustment to the diode amplifiers to have the right
# dynamic range for the integrated counts.
# The safety shutter is open, so the adjustment is made on i0. The resulting
# gainfactor is applied also to i1 which should be around the same sensitivity.

def adjust_i0_i1_gain() '{
  ct 0.1
	wcgain i1 wcreadgain(i0)
}'

# prepares the centring: set in light and move zoom to 0
def eprodc_prepare_centring '{
  SCMinidiffGetControl
  lightin
  mv zoom 0
}'

def egui_logmsg(msgtext) '{
  global eprodc_log_message
  if (EPRODC_DEBUG==1)
    printf("egui_logmsg %s\n",msgtext)
  eprodc_log_message=msgtext
}'

def egui_fatal(msgtext) '{
  unglobal eprodc_fatal_collect
  global eprodc_fatal_collect[]
  if (EPRODC_DEBUG==1)
    printf("egui_fatal %s\n", msgtext)
  eprodc_fatal_collect["message"]=msgtext
  eprodc_fatal_collect["code"]=-1
  eprodc_log_message=msgtext
}'


# below is simulation code for working without beam
global SIMUL_IMAGEFILE SIMUL_IMAGE_CTR
def simul_ccdstart '{
  global _ccd_retDict
  if (EPRODC_DEBUG==1)
    print "simul_ccdstart $1  $2  $3  $4  $5"
  egui_logmsg("Simulated detector is integrating")
  sleep(0.1)
  _ccd_retDict["code"]=0
 
}'

def simul_ccdread '{
  global _ccd_retDict
  egui_logmsg("Simulated detector is reading out")
  sleep(0.1)
  _ccd_retDict["code"]=0

}'

def simul_ccdsave '{
  global _ccd_retDict
  global SIMUL_IMAGEFILE
  SIMUL_IMAGE_CTR = SIMUL_IMAGE_CTR%2
  egui_logmsg("Simulated detector is writing image")
  cmd = sprintf("cp %s/test%d.img %s.img",COLLECT_SEQ["fileinfo"]["directory"],SIMUL_IMAGE_CTR,SIMUL_IMAGEFILE)
  unix(cmd)
  sleep(0.1)
  SIMUL_IMAGE_CTR++
 
}'

def simul_ccdflush '{
  global _ccd_retDict
  SIMUL_IMAGE_CTR = SIMUL_IMAGE_CTR%2
  egui_logmsg("Simulated detector is writing image (last image)")
  cmd = sprintf("cp %s/test%d.img %s.img",COLLECT_SEQ["fileinfo"]["directory"],SIMUL_IMAGE_CTR,SIMUL_IMAGEFILE)
  unix(cmd)
  sleep(0.1)
  SIMUL_IMAGE_CTR++
 
}'

def simul_ccdcleanup '{
  if (EPRODC_DEBUG==1)
    print "ccd_cleanup: well nothing to do for a simulated detector"
}'

def ccd_simulate_on '{
  cdef("ccdstart","simul_ccdstart","_pxccd",0x20)
  cdef("ccdread","simul_ccdread","_pxccd",0x20)
  cdef("ccdcleanup","simul_ccdcleanup","_pxccd",0x20)
  cdef("ccdsave","simul_ccdsave","_pxccd",0x20)
  cdef("ccdflush","simul_ccdflush","_pxccd",0x20)  
  cdef("ccdfile","simul_filename","_pxccd",0x20)  
  cdef("ccdprep","simul_prep","_pxccd",0x20)  
}'

def simul_prep(dark,osc_start,osc_range,exptime,npass,comment) '{
 if (EPRODC_DEBUG==1)
    print "simul_prep" 
}'

def simul_filename(cpars,frame) '{
  global SIMUL_IMAGEFILE SIMUL_IMAGENUMBER
  SIMUL_IMAGENUMBER = cpars["start_image_number"] + frame      
  SIMUL_IMAGEFILE =  sprintf("%s/%s_%d_%03d",cpars["fileinfo"]["directory"],\
                                            cpars["fileinfo"]["prefix"],\
                                            cpars["fileinfo"]["run_number"],\
                                            SIMUL_IMAGENUMBER)
  if (EPRODC_DEBUG==1)print "*filename*** " SIMUL_IMAGEFILE
  return(SIMUL_IMAGEFILE)
}'

def ccd_simulate_off '{
  cdef("","","_pxccd","delete")
}'


def eprodc_energy_scan_status(msg) '{
  global eprodc_energy_scan_msg
  eprodc_energy_scan_msg=msg
}'

def eprodc_check_safety_shutter() '{
  return shstate()
}'

def prodc_prepare_beamline '{
  global COLLECT_SEQ

  specific_prepare_beamline

  # move to centred position (if it exists inside COLLECT_SEQ)
  getangles
  for (motname in COLLECT_SEQ["centred_pos"]) {
    motnum = motor_num(motname)
    if (motnum>0) {
      motpos=COLLECT_SEQ["centred_pos"][motname]
      printf("  - moving %s to %f\n", motname, motpos)
      A[motnum]=motpos
    } else {
        printf("  - unknown motor %s, breaking\n", motname)
        getangles
        break
    }
  }
  move_em; waitmove
}'

def prodc_finish_beamline '{
  specific_finish_beamline
}'

##############################################################################
# backwards compatibility macros... should be tidied up and removed
# after testing that we can live without them!
#
#

def prodc_quick_realign '{
  global curr_intensity start_intensity
  start_intensity = curr_intensity
  curr_intensity = 0

  check_beam()

  shopen
  specific_quick_realign
  shclose
}'

def prodc_normal_realign '{
  global curr_intensity start_intensity
  start_intensity = curr_intensity
  curr_intensity = 0
  shopen
  specific_normal_realign
  shclose
}'

def prodc_open_safety_shutter '{
  print "reference to old command prodc_open_safety_shutter, redirected to shopen"
  shopen
}'

def prodc_close_safety_shutter '{
  print "reference to old command prodc_close_safety_shutter, redirected to shclose"
  shclose
}'

def open_ms_shutter '{
  print "reference to old command open_ms_shutter, redirected to msopen"
  msopen
}'

def close_ms_shutter '{
  print "reference to old command close_ms_shutter, redirected to msclose"
  msclose
}'

#%MACROS%
#%IMACROS%
#%TOC%
#%AUTHOR% BLISS
#$Revision: 1.62 $$Date: 2013/08/19 12:38:35 $
#%END%
#%LOG%
#$Log: mxcollect.mac,v $
#Revision 1.62  2013/08/19 12:38:35  beteva
#Added local_skip_images
#
#Revision 1.61  2011/11/08 15:52:13  guijarro
#removed check for overlap with helical, non needed anymore
#
#Revision 1.60  2010/06/29 16:36:43  beteva
#added check for overlap and position when helical oscillation
#
#Revision 1.59  2010/06/04 06:43:43  guijarro
#added code for moving motors specified in comment field
#
#Revision 1.58  2010/05/17 12:43:13  guijarro
#added code for not closing safety shutter while in the middle of a queue
#
#Revision 1.57  2010/03/29 07:49:39  guijarro
#adapted to latest version of beamline parameters
#
#Revision 1.55  2010/02/16 15:32:09  spruce
#Change the inverse beam flag. This was changed at some point in mxcube, probably to reflect the same terminology used in the ISPyB enumerated record entry.
#This broke the inverse beam option because spec no longer recognised it.
#
#Revision 1.54  2010/02/09 15:54:17  blissadm
#adapted to latest mxutils.mac
#
#Revision 1.53  2009/11/05 14:39:54  spruce
#corrected the simulated ccdflush so the adsc suffix is added to the last image
#
#Revision 1.52  2009/04/06 15:28:28  guijarro
#added test to find if we have a microdiff or a minidiff
#
#Revision 1.51  2009/04/02 08:35:41  guijarro
#added warning message for HELICAL_OSCIL
#load MUSST program
#
#Revision 1.47  2008/07/28 12:20:42  guijarro
#do not execute prodc_set_mono if energy change is less than 2 eV
#
#Revision 1.46  2008/07/09 15:15:08  guijarro
#added check_beam before quick realign
#
#Revision 1.45  2008/06/24 15:56:04  spruce
#make gain adjustment for autogain
#
#Revision 1.44  2008/03/25 12:46:40  blissadm
#if egui_fatal is called also set the log message variable.
#
#Revision 1.43  2008/02/18 17:15:45  spruce
#bug fixes following test of waitforbeam
#to enable this feature set the global I0THRESHOLD in the setup
#
#Revision 1.42  2008/01/17 16:41:13  blissadm
#added halfcorner calculation, just in case we want to display it in mxcube
#
#Revision 1.40  2007/12/03 16:14:04  spruce
#add a waitmove before ccdstart to be sure that any spindle movements are finished. This solves a problem of image smearing for adsc Q210/315 detectors where teh dark time is sensitive to the integration time.
#
#Revision 1.39  2007/10/31 14:51:00  guijarro
#added CURRENT_FRAME_NUMBER global variable (mainly for microdiff)
#
#Revision 1.38  2007/10/03 13:04:47  spruce
#added methods for storing undulator gaps, kappa positions, beam size/divergence/polarisation in the database. kappSpecMne and phiSpecMne should be defined as beamline parameters as motors in mxlocal as undulatorGap0, 1, 2 depending on the number of undulators. Currently nothing is returned for beamsize/divergence etc until further information is given by the mx group.
#
#Revision 1.37  2007/05/22 13:17:34  gabadinh
#Had to preempt the error message before calling SCMinidiffGetControl because this macro does an exit if something goes wrong...
#
#Revision 1.36  2007/05/22 11:56:14  gabadinh
#Renamed the variable/channel eprodc_fatal_message to eprodc_fatal_collect, also from string to an associative-array; it is also cleared at the beggining of every data collection
#
#Revision 1.35  2007/05/11 15:03:32  gabadinh
#added several beamline values to the data collection macro return associative-array:
#-transmission
#-resolution
#-detector distance
#-wavelength
#-beam centre
#
#Revision 1.34  2007/03/30 13:44:07  gabadinh
#prodc_prepare_beamline moves some minidiff motors to the centred position, if defined in the parameters
#
#Revision 1.33  2007/03/06 08:34:34  guijarro
#refactoring + added fatal log function
#
#Revision 1.32  2007/01/22 16:28:26  guijarro
#added special order when modifying CURRENT_IMAGE,
#this is very important for mxCuBE
#
#Revision 1.31  2007/01/18 15:04:56  spruce
#a few changes to make the test_mxcollect work again
#
#Revision 1.30  2007/01/18 13:12:41  spruce
#change CURRENT_IMAGE global from string to array
#
#Revision 1.29  2007/01/18 12:37:47  spruce
#took away phi cleanup code and call the phi cleanup in oscilPX directly
#
#Revision 1.28  2006/12/14 16:29:40  guijarro
#makes sure that phi motor get its speed & velocity back to normal
#in the cleanup
#
#Revision 1.27  2006/12/07 17:17:34  spruce
#added code to stop data collection elegantly in the middle of a wedge
#
#Revision 1.26  2006/12/06 16:05:17  spruce
#corrections for stills after testing on ID14-2
#
#Revision 1.24  2006/11/28 16:45:54  spruce
#inverse beam flag changed in mxcube, made compatible in this version
#
#Revision 1.23  2006/11/21 16:41:19  spruce
#change format of integrated counts to %g
#
#Revision 1.22  2006/10/09 09:30:17  spruce
#add a dummy definition of the hook to save it being created in all local
#macros. This means the local definition must be placed to overload the dummy
#macro when spec starts
#
#Revision 1.21  2006/10/03 13:33:15  spruce
#>> a little clean up. This has been running for 4 days on ID14-4 including pipeline demos and users. No detector problems reported.
#return code changed back to 2005 to remove warning in mxCuBE
#
#Revision 1.20  2006/09/28 15:36:04  spruce
#bugs fixed, compatible with mxadsc 1.17
#
#Revision 1.19  2006/09/27 13:26:30  spruce
#open the safety shutter after darks
#make the automatic gain adjustment of the amplifiers
#
#Revision 1.18  2006/09/26 12:47:57  spruce
#pick up detector errors and abort if necessary
#added generic hook to do specific calculations e.g for best and raddose.
#
#Revision 1.17  2006/09/12 12:55:06  guijarro
#no more call ccdcleanup in cleanup : this will be done when
#necessary by adsc macros (in mxadsc.mac).
#
#Revision 1.16  2006/08/24 16:45:56  gabadinh
#reads default_number_of_passes from MxBcmPars, not from MxCollectPars
#commented out the current detector binning mode
#
#Revision 1.15  2006/08/24 12:28:04  blissadm
#added default exposure time to list of vars for mxCuBE. needs to be added to mxlocal.xml.
#removed setting of detector mode to the default. new check to verify if the requested binning mode is equal to the default.
#
#Revision 1.14  2006/07/28 15:29:52  guijarro
#bug fix with overlap when 'skip images' was ON ;
#added call to prodc_finish_beamline in cleanup,
#and call datacollection_cleanup instead of shclose
#and prodc_finish_beamline at the end of data collection.
#
#Revision 1.13  2006/07/11 14:11:54  spruce
#fixes for validating collect parameters
#
#Revision 1.12  2006/07/10 15:37:02  guijarro
#fixed call to setMxSpecPars
#
#Revision 1.11  2006/07/05 15:18:04  spruce
#added new validation check for overlap=1
#
#Revision 1.10  2006/06/14 08:45:04  gabadinh
#added backlash while checking the limits of detector distance motor
#added moving the detector if distance is given
#
#Revision 1.9  2006/06/06 14:16:50  spruce
#add suffix to imagefile variable
#
#Revision 1.8  2006/05/31 16:08:06  spruce
#added new parameters for phi_speed, exposure time default number of passes
#to update_collect_parameters function for xmCuBE and DNA
#
#Revision 1.7  2006/05/15 11:41:26  spruce
#bugfix DNA sends resolution with the upper keyword instead of lower
#new function validate_collect_parameters to filter user mistakes
#
#Revision 1.6  2006/05/11 11:43:20  spruce
#small bug, dark fixed to on every time
#
#Revision 1.5  2006/05/03 15:26:43  spruce
#added limit checking if trying to move detector out of limits
#
#Revision 1.4  2006/04/11 14:08:27  guijarro
#bug fix for "skip images" feature :
#the filename given to the file_info function was incomplete (missing extension)
#
#Revision 1.3  2005/11/20 18:20:10  blissadm
#new code with inverse beam functionality
#
#Revision 1.2  2005/11/08 17:31:28  beteva
#added some COLLECT_PARS for the dark time calculations
#
#Revision 1.1  2005/11/08 17:26:20  beteva
#Initial revision