Esempio n. 1
0
def goifpc(nexp=1):

    """
    Take an exposure or a sequence of exposures with the focal plane camera. 
    If the keywords sequence and trigtime are set, then takes a sequence.

    Parameters
    ----------
    nexp : int
        Desired number of exposure. 

    Examples
    --------
    
    Take a single exposure with the blue camera

    >>> Blue.goifpc(1)

    """
    numberOfExposures = nexp

    # start keyword monitoring

    server = 'kfcs'
    try:
            loutfile = ktl.cache(server,'LASTFILE')
            startex = ktl.cache(server,'STARTEX')
            stopex = ktl.cache(server,'STOPEX')
            ttime = ktl.cache(server,'EXPTIME')
            status = ktl.cache(server,'STATUS')
            trigtime = ktl.cache(server,'TRIGTIME')
            sequence = ktl.cache(server,'SEQUENCE')
            counter = ktl.cache(server,'COUNTER')
            closed = ktl.cache(server,'CLOSED')
    except:
            raise RuntimeError("Failed to read detector keywords. KFCS might not be running")


    monitoredKeywords = (loutfile, startex, stopex, ttime, status,
                         trigtime, sequence, counter, closed)

    setupMonitoring(monitoredKeywords, wait=True)

    n = 0
    errcnt = 0

    # is FPC open (not closed)?

    if closed == 0:

        # main loop

        while n< numberOfExposures and errcnt < 2:

            # wait for current exposure to end
            status.waitFor('!=Busy')

            exposureTime = ttime
            imno = counter
            ctim = time.asctime()

            say ('%s: Taking %.3f s exposure %d of %d (image # %d)' %
                    (ctim, exposureTime, n+1, numberOfExposures, imno))

            # start a new exposure

            startex.write(1)

            say("Exposing")

            # what is the wait time?
            seq = sequence
            triggerTime = trigtime

            waitTime = (exposureTime+triggerTime)*seq

            if waitTime < 1:
                    waitTime = 1
            time.sleep(waitTime+1)


            status.waitFor('!=Busy')

            stat = status
            
            if stat == 'OK':
                say('Last file is: ' + loutfile['ascii'])
                say("Readout complete")
                n += 1
            else:
                say("Error reading out, aborting!")
                stopex.write(1)
                errcnt += 1

        say('Exposure sequence complete')
    else:
        say('Cannot take exposure, camera closed.')
Esempio n. 2
0
def pwaveb(pwave=None, cwave=None, move=True, quiet=False):
    """
    Show or set the peak wavelength

    Parameters
    ----------
    cwave : float
        Desired central wavelength in Angstroms    
    pwave : float
        Peak wavelength 
    move : boolean
        Set to false to only modify the target without moving the stages
    quiet : boolean
        Set to true to disable progress bar

    Examples
    --------

    set the peak wavelength to 6700 Angstrom

    >>> Blue.pwaveb(pwave=6700)
    """

    server = 'kcwi'
    cwavetarg = ktl.cache(server,'BCWAVETARG')
    pwavetarg = ktl.cache(server,'BPWAVETARG')

    monitoredKeywords = (cwavetarg, pwavetarg)
    setupMonitoring(monitoredKeywords, wait=True)

    # retrieve the name of the current grating

    currentgrating = gratingb()

    if currentgrating == None or currentgrating == 'None':
        say("There is no grating in the beam")
        return

    # instantiate a grating object

    g = grating(currentgrating)

    if pwave is not None and is_in_filling_position() != False:
       say("KCWI might be in filling position, moves are not allowed")
       return

    if pwave is not None:

        pwavetarg.write(pwave)

        cwave = cwaveb()

        # calculate the required angles

        g.calc_from_wavelengths(cwave=cwave, pwave=pwave)

        # use multiprocesing to run camera and grating at the same time

        # camera angle
        camangleb(angvalue=g.camang, move=move, quiet=quiet)
        # grating angle
        grangleb(angvalue=g.grangle, move=move, quiet=quiet)
        
        # return values

        return pwave #, pwave #, g.camang, g.grangle

    else:

        # the function was called without a requested wavelength,
        # so we return the calculated wavelength based on angles

        camangle = float(camangleb())
        grangle = float(grangleb())

        g.calc_from_angles(camang=camangle, grangle=grangle)

        return g.pwave #, g.pwave, camangle, grangle
Esempio n. 3
0
def goib(nexp, dark=False):

    """
    Take an exposure or a sequence of exposures with the blue camera.

    Parameters
    ----------
    nexp : int
        Desired number of exposure. 
    dark : boolean
        If True, do not open shutter
    imtype : string
        twiflat will set the parameters for a sky flat

    Examples
    --------
    
    Take a single exposure with the blue camera

    >>> Blue.goib(1)

    """

    numberOfExposures = int(nexp)

    # start keyword monitoring

    server = 'kbds'
    try:
        exposeip = ktl.cache(server,'EXPOSIP')
        rdoutip = ktl.cache(server,'RDOUTIP')
        loutfile = ktl.cache(server,'LOUTFILE')
        startex = ktl.cache(server,'STARTEX')
        todisk = ktl.cache(server,'TODISK')
        ttime = ktl.cache(server,'TTIME')
        autoshut = ktl.cache(server,'AUTOSHUT')
        frameno = ktl.cache(server,'FRAMENO')
        imtype = ktl.cache(server,'IMTYPE')
        groupidk = ktl.cache(server,'GROUPID')
    except:
        raise RuntimeError("Failed to read detector keywords. KBDS might not be running")

    # get the date obs
    try:
        dateobsk = ktl.cache('dcs','DATE-OBS')
        dateobs = dateobsk.read()
    except:
        dateobs = 'UNKNOWN'
        

    monitoredKeywords = (exposeip, rdoutip, loutfile, startex,
                         todisk, ttime, autoshut, frameno,imtype, groupidk)

    setupMonitoring(monitoredKeywords, wait=True)

    n = 0

    td = int(todisk['ascii'])

    # create GROUPID keyword
    groupid = "%s-%d" % (dateobs,frameno)
    groupidk.write(groupid)

    if not td:
        say("WARNING: todisk keyword prevents saving images")

        
    if dark==True and float(ttime.read())>0:
        say("Disabling autoshutter: images will be dark")
        imtype.write('DARK')
        autoshut.write(0)

    if float(ttime.read()) == 0:
        say("Exposure time is zero: images will be biases")
        imtype.write('BIAS')

    while n < numberOfExposures:

          # wait for current exposure to end
        startex.waitFor('==0')
        exposeip.waitFor('==0')
        rdoutip.waitFor('==0')
        loutstart=loutfile['ascii']
        cond = "!='"+loutfile['ascii']+"'"
        ctim = time.asctime()
        exposureTime = ttime
        imno = frameno

        say ('%s: Taking %.3f s exposure %d of %d (image # %d)' % 
             (ctim, exposureTime, int(n+1), int(numberOfExposures), imno))

        # start a new exposure

        startex.write(1)

        # wait for exposure to start
        exposeip.waitFor('==1',timeout=20)
        say("Exposing")

        # what is the exposure time?
        exposureTime=float(ttime['ascii'])

        # wait for readout
        rdoutip.waitFor('==1',timeout=exposureTime+10)
        say("Reading out")

        # wait for readout
        rdoutip.waitFor('==0',timeout=450)
        say("Readout complete")

        # only test if writing to disk
        if td:
            # wait for last file to change
            loutfile.waitFor(cond, timeout=120)

            say('Last file is: '+loutfile['ascii'])

        n=n+1

        say('Exposure sequence complete')


        if dark==True:
            autoshut.write(1)
Esempio n. 4
0
def nsmaskb(target=None, move=True, quiet=True):
    """
    Reads or modify the position of the nod and shuffle mask


    Parameters
    ----------
    target : string
        Desired position. Valid values are: "Open", "Test", "Mask"
    move : boolean
        Set to false to only modify the target without moving the N&S mask
    quiet : boolean
        Set to disable progress bar


    Examples
    --------
    Prints the position of the nod and shuffle mask

    >>> Blue.nsmasbk()

    Set the nod and shuffle mask to Mask

    >>> Blue.nsmaskb(target="Mask")

    """

    server = 'kbms'
    nasname = ktl.cache(server, 'NASNAME')   # current 
    nastargn = ktl.cache(server, 'NASTARGN') # target 
    nasmove = ktl.cache(server, 'NASMOVE')   # initiate the move
    nasstatus = ktl.cache(server, 'NASSTATUS') # values?

    monitoredKeywords = (nasname, nastargn,nasmove,nasstatus)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # if called with an empty string, return the current slicer
    if target==None:
        nas = nasname.ascii
        lg.info("kcwiServer: Returning nod and shuffle value '%s'" % (nas))
        return nas

    # if the requested target is the same as the current, do not move
    if target==nasname.ascii and move==True:
        say("N&S: Target is the same as requested. No move needed.")
        return

    # check if move is possible
    checkIfMoveIsPossible(nasstatus)

    # initiate the move
    nastargn.write(target)

    # if move is True, then force a move
    if move==True:
        nasmove.write(1)
        if not quiet:
            p = AnimatedProgressBar(end=100, width=standardWidth)
            ktl.monitor(server,'NASPROG',ProgressCallback,p)
        # fmove expressions
        moving        = '$kbms.nasmove == 1'
        not_moving    = '$kbms.nasmove == 0'
        target_reached = '$kbms.nasname == $kbms.nastargn'

        moving = ktl.Expression(moving)
        not_moving = ktl.Expression(not_moving)
        target_reached = ktl.Expression(target_reached)

        # wait for moving
        result = moving.wait(timeout = timeOutMove)
        if result == False:
            raise RuntimeError("Mechanism %s did not start moving within %d seconds" % ("N&S Mask", timeOutMove))

        # wait for not moving
        not_moving.wait(timeout=240)

        # check for successful move
        time.sleep(5)

        checkSuccess(statusKeyword=nasstatus, mechanism="N&S Mask", targetReachedExpression=target_reached, successStatus="OK")
Esempio n. 5
0
def cwaveb(cwave=None, pwave=None, move=True, quiet=False):
    """
    Show or set the central (and optionally the peak) wavelength

    Parameters
    ----------
    cwave : float
        Desired central wavelength in Angstroms    
    pwave : float
        Peak wavelength (if desired and different from cwave)
    move : boolean
        Set to false to only modify the target without moving the stages
    quiet : boolean
        Set to true to disable progress bar

    Examples
    --------

    set the central wavelength to 6700 Angstrom

    >>> Blue.cwaveb(cwave=6700)
    """

    server = 'kcwi'
    cwavetarg = ktl.cache(server,'BCWAVETARG')
    pwavetarg = ktl.cache(server,'BPWAVETARG')

    monitoredKeywords = (cwavetarg, pwavetarg)
    setupMonitoring(monitoredKeywords, wait=True)


    # retrieve the name of the current grating

    currentgrating = gratingb()

    if currentgrating == None or currentgrating == 'None':
        say("There is no grating in the beam")
        return

    # instantiate a grating object

    g = grating(currentgrating)

    if cwave is not None and is_in_filling_position() != False:
        say("KCWI might be in filling position, moves are not allowed")
        return


    if cwave is not None:
        # update target

        cwavetarg.write(cwave)
        if pwave is not None:
            pwavetarg.write(pwave)
        else:
            pwavetarg.write(cwave)

        # calculate the required angles

        g.calc_from_wavelengths(cwave=cwave, pwave=pwave)

        # camera angle
        p1 = threading.Thread(target = camangleb, args = (g.camang, True, quiet))

        # grating angle
    
        p2 = threading.Thread(target = grangleb, args = (g.grangle, True, quiet))
        p1.start()
        say("Camera motion started")
        p2.start()
        say("Grating motion started")
        p1.join()
        p2.join()

        # return values

        return cwave #, pwave #, g.camang, g.grangle

    else:

        # the function was called without a requested wavelength, 
        # so we return the calculated wavelength based on angles

        camangle = float(camangleb())
        grangle = float(grangleb())

        g.calc_from_angles(camang=camangle, grangle=grangle)

        return g.cwave #, g.pwave, camangle, grangle
Esempio n. 6
0
def grangleb(angvalue=None, move=True, quiet=False):
    """
    Reads or set the blue channel grating angle

    Parameters
    ----------
    angvalue : float
        Desired grating angle in degrees
    move : boolean
        Set to false to only modify the target without moving the grangle
    quiet : boolean
        Set to disable progress bar

    Examples
    --------
    Prints the name of the current camangle

    >>> Blue.grangleb()

    Go to 10 degrees

    >>> Blue.grangleb(angvalue=10)

    """
    timeOutComplete = 180.                  # extra time for grating angle
    server = 'kbes'
    grtargp = ktl.cache(server,'GRTARGP')   # target position (0,1 or 2)
    grangle = ktl.cache(server,'GRANGLE')   # current angle
    grtrgang = ktl.cache(server,'GRTRGANG') # target angle
    grstatus = ktl.cache(server,'GRSTATUS') 
    gstatus = ktl.cache(server,'GSTATUS')
    grmove = ktl.cache(server,'GRMOVE')
    monitoredKeywords = (grtargp, grangle, grtrgang, grstatus,gstatus, grmove)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # if called with an empty string, return the current filter, otherwise set the filter
    if angvalue==None:
        result = grangle.ascii
        lg.info("kcwiServer: Returning grating angle '%s'" % (result))
        return result

    # if the requested target is the same as the current, do not move
    if abs(float(angvalue)-grangle) < 0.01:
        say("Grating angle: Target equals current, no move needed.")
        return grangle.ascii

    # check if move is possible
    checkIfMoveIsPossible(gstatus)
    checkIfMoveIsPossible(grstatus)

    # set the target. This is done both for move=True and move=False
    grtrgang.write(angvalue)

    # stop here if we are not asking for a move

    if move == False:
        return

    # check that we are in angle mode

    # if we are not in angle mode, changing the angle mode initiates a move
    # if we are in angle mode, we need to issue a move command

    if grtargp != 2:
        sys.stdout.write("Setting grating rotator to angle mode\n")
        grtargp.write(2)

    grmove.write(1)

    # move expressions
    moving        = '$kbes.grstatus == "Moving"'
    not_moving    = '$kbes.grstatus == "Move complete"'
    target_reached = '$kbes.grposerr < $kbes.grtolopt'

    moving = ktl.Expression(moving)
    not_moving = ktl.Expression(not_moving)
    target_reached = ktl.Expression(target_reached)

    # wait for moving
    result = moving.wait(timeout = timeOutMove)
    if not quiet:
        p = AnimatedProgressBar(end=100, width=standardWidth)
        ktl.monitor(server,'GRPROG',ProgressCallback,p)

    if result == False:
        raise RuntimeError("Mechanism %s did not start moving within %d seconds"
                % ("Grating rotator", timeOutMove))

    # wait for not moving
    not_moving.wait(timeout=timeOutComplete)

    # check for successful move
    time.sleep(2)
    checkSuccess(statusKeyword=grstatus, mechanism="Grating rotator",
            targetReachedExpression=target_reached, successStatus="Move")
    #if abs(artposerr) > arttol:
    #    say("Warning: The required encoder position has NOT been reached")

    # return value
    result = grangle.ascii
    return result
Esempio n. 7
0
def camangleb(angvalue=None, move=True, quiet=False):
    """
    Reads or set the blue channel articulation stage angle

    Parameters
    ----------
    angvalue : float
        Desired camera angle in degrees
    move : boolean
        Set to false to only modify the target without moving the camangle
    quiet : boolean
        Set to disable progress bar

    Examples
    --------
    Prints the name of the current camangle

    >>> Blue.camangleb()

    Go to 10 degrees

    >>> Blue.camangleb(angvalue=10)

    Modify the camangle target keyword but do not move

    >>> Blue.camangleb(angvalue=10, move=False)

    """
    timeOutComplete = 180.              # extra time for cam angle
    server = 'kbms'
    #gname = ktl.cache(server,'GNAME')   # current grating
    arttarg = ktl.cache(server,'ARTTARG') # target encoder
    arttargang = ktl.cache(server,'ARTTARGANG') # target angle
    artmove = ktl.cache(server,'ARTMOVE')   # initiate the move
    artstatus = ktl.cache(server,'ARTSTATUS') 
    artenc = ktl.cache(server,'ARTENC')
    artang = ktl.cache(server,'ARTANG')
    artposerr = ktl.cache(server,'ARTPOSERR')
    arttol = ktl.cache(server,'ARTTOL')

    monitoredKeywords = (arttarg,arttargang,artmove,artstatus,artenc,artang,artposerr,arttol)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # if called with an empty string, return the current filter, otherwise set the filter
    if angvalue==None:
        result = artang.ascii
        lg.info("kcwiServer: Returning camera angle '%s'" % (result))
        return result

    # if the requested target is the same as the current, do not move
    if abs(float(angvalue)-artang) < 0.001 and move==True:
        say("Articulation stage: Target equals current, no move needed.")
        return artang.ascii

    # check if move is possible
    checkIfMoveIsPossible(artstatus)

    # check if we are in filling position
    if is_in_filling_position() != False:
        say("KCWI might be in filling position, moves are not allowed")
        return -1

    # set the target. This is done both for move=True and move=False
    arttargang.write(angvalue)

    # if move is True, then force a move
    if move==True:
        artmove.write(1)

        # move expressions
        moving        = '$kbms.artmove == 1'
        not_moving    = '$kbms.artmove == 0'
        target_reached = '$kbms.artang == $kbms.arttargang'

        moving = ktl.Expression(moving)
        not_moving = ktl.Expression(not_moving)
        target_reached = ktl.Expression(target_reached)

        # wait for moving
        result = moving.wait(timeout = timeOutMove)
        if not quiet:
            p = AnimatedProgressBar(end=100, width=standardWidth)
            ktl.monitor(server,'ARTPROG',ProgressCallback,p)

        if result == False:
            raise RuntimeError("Mechanism %s did not start moving within %d seconds" % ("Articulation stage", timeOutMove))

        # wait for not moving
        not_moving.wait(timeout=timeOutComplete)

        # check for successful move
        time.sleep(5)
        checkSuccess(statusKeyword=artstatus, mechanism="Articulation stage", targetReachedExpression=None, successStatus="OK")
        if abs(artposerr) > arttol:
            say("Warning: The required angle has NOT been reached")

        # return value
        result = artang.ascii
        lg.info("kcwiServer: Returning articulation stage angle '%s'" % (result))
        return result
Esempio n. 8
0
def gratingb(target=None, move=True):
    """
    Reads or set the blue channel grating

    Parameters
    ----------
    target : string
        Desired grating. Values are: TBD
    move : boolean
        Set to false to only modify the target without moving the grating

    Examples
    --------
    Prints the name of the current grating

    >>> Blue.gratingb()

    Insert the L grating

    >>> Blue.gratingb(target="L")

    Modify the grating target keyword but do not move

    >>> Blue.gratingb(target="H2", move=False)

    """
    timeOutComplete = 360.              # extra time for grating exchange
    server = 'kbes'
    gname = ktl.cache(server,'GNAME')   # current grating
    gtargn = ktl.cache(server,'GTARGN') # target grating
    gmove = ktl.cache(server,'GMOVE')   # initiate the move
    gstatus = ktl.cache(server,'GSTATUS') 
    movemode = ktl.cache(server,'MOVEMODE')

    monitoredKeywords = (gname, gtargn, gstatus, gmove, movemode)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # if called with an empty string, return the current filter, otherwise set the filter
    if target==None:
        grating = gname.ascii
        lg.info("kcwiServer: Returning grating value '%s'" % (grating))
        return grating

    # if the requested target is the same as the current, do not move
    if target==gname.ascii and move==True:
        say("Grating: Target equals current, no move needed.")
        return gname.ascii

    # check if move is possible
    checkIfMoveIsPossible(gstatus)

    # reset move mode to 0
    currentMoveMode = movemode.ascii
    changeMoveMode(movemode=movemode,mode=0)

    # set the target. This is done both for move=True and move=False
    gtargn.write(target)

    # if move is True, then force a move
    if move==True:
        gmove.write(1)

        # fmove expressions
        moving        = '$kbes.gmove == 1'
        not_moving    = '$kbes.gmove == 0'
        target_reached = '$kbes.gname == $kbes.gtargn'

        moving = ktl.Expression(moving)
        not_moving = ktl.Expression(not_moving)
        target_reached = ktl.Expression(target_reached)

        # wait for moving
        result = moving.wait(timeout = timeOutMove)
        if result == False:
            raise RuntimeError("Mechanism %s did not start moving within %d seconds" % ("Grating", timeOutMove))

        # wait for not moving
        not_moving.wait(timeout=timeOutComplete)

        # check for successful move
        time.sleep(2)
        checkSuccess(statusKeyword=gstatus, mechanism="Grating",
                targetReachedExpression=target_reached, successStatus="Success:")

        # return value
        grating = gname.ascii
        lg.info("kcwiServer: Returning grating value '%s'" %grating)
        return grating


    # reset move mode
    if currentMoveMode == 1:
        changeMoveMode(movemode=movemode, mode=1)
Esempio n. 9
0
def filterb(target=None, move=True):
    """
    Reads or set the blue channel filter

    Parameters
    ----------
    target : string
        Desired filter. Values are: TBD
    move : boolean
        Set to false to only modify the target without moving the filter

    Examples
    --------
    Prints the name of the current filter

    >>> Blue.filterb()

    Insert the B1 filter

    >>> Blue.filterb(target="B1")

    Modify the filter target keyword but do not move

    >>> Blue.filterb(target="B1", move=False)

    """
    timeOutComplete = 180.              # extra time for filter exchange
    server = 'kbes'
    fname = ktl.cache(server, 'FNAME')   # current filter
    ftargn = ktl.cache(server, 'FTARGN') # target filter
    fmove = ktl.cache(server, 'FMOVE')   # initiate the move
    fstatus = ktl.cache(server, 'FSTATUS') # values: OK, MOVING, ERROR, INIT_ERROR
    movemode = ktl.cache(server, 'MOVEMODE')

    monitoredKeywords = (fname, ftargn, fstatus, fmove, movemode)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # if called with an empty string, return the current filter, otherwise set the filter
    if target==None:
        filter = fname.ascii
        lg.info("kcwiServer: Returning filter value '%s'" %filter)
        return filter

    # if the requested target is the same as the current, do not move
    if target==fname.ascii and move==True:
        say("Filter: Target equals current, no move needed.")
        return fname.ascii

    # check if move is possible
    checkIfMoveIsPossible(fstatus)

    # reset move mode to 0
    currentMoveMode = movemode.ascii
    changeMoveMode(movemode=movemode,mode=0)

    # initiate the move
    ftargn.write(target)

    # if move is True, then force a move
    if move==True:
        fmove.write(1)

        # fmove expressions
        moving        = '$kbes.fmove == 1'
        not_moving    = '$kbes.fmove == 0'
        target_reached = '$kbes.fname == $kbes.ftargn'

        moving = ktl.Expression(moving)
        not_moving = ktl.Expression(not_moving)
        target_reached = ktl.Expression(target_reached)

        # wait for moving
        result = moving.wait(timeout = timeOutMove)
        if result == False:
            raise RuntimeError("Mechanism %s did not start moving within %d seconds" % ("Filter", timeOutMove))

        # wait for not moving
        not_moving.wait(timeout=timeOutComplete)

        # check for successful move
        time.sleep(2)
        checkSuccess(statusKeyword=fstatus, mechanism="Filter",
                targetReachedExpression=target_reached, successStatus="Success:")

        # return value
        filter = fname.ascii
        lg.info("kcwiServer: Returning filter value '%s'" %filter)
        return filter

    # reset move mode
    if currentMoveMode == 1:
        changeMoveMode(movemode=movemode, mode=1)
Esempio n. 10
0
def focusb(target=None, move=True, quiet=False):
    """
    Reads or set the blue camera focus

    Parameters
    ----------
    target : float
        Desired focus value in mm.
    move : boolean
        Set to False to just set target
    quiet : boolean
        Set to disable progress bar

    Examples
    --------
    Print the current focus value
    >>> Blue.focusb

    Set the current focus value to 1.5mm
    >>> Blue.focusb(target=1.5)

    """
    server = 'kbms'
    foctargmm = ktl.cache(server,'FOCTARGMM') # target encoder
    focmove = ktl.cache(server,'FOCMOVE')   # initiate the move
    focstatus = ktl.cache(server,'FOCSTATUS') 
    focmm = ktl.cache(server,'FOCMM')
    focposerr = ktl.cache(server,'FOCPOSERR')
    focmmerr = ktl.cache(server,'FOCMMERR')
    foctol = ktl.cache(server,'FOCTOL')
    focenc = ktl.cache(server,'FOCENC')
    foctarg = ktl.cache(server,'FOCTARG')

    monitoredKeywords = (foctargmm,focmove,focstatus,focmm,focposerr,focmmerr,foctol,focenc,foctarg)

    # set wait = False if you can accept undefined keywords (for simulation, for example)
    setupMonitoring(monitoredKeywords, wait=True)

    # if called with an empty string, return the current value
    if target==None:
        result = focmm.ascii
        lg.info("kcwiServer: Returning focus value '%s'" % (result))
        return result

    # set the target. This is done both for move=True and move=False
    foctargmm.write(target,wait=True)
    time.sleep(2)

    # if the requested target is the same as the current, do not move
    #print(float(focenc.ascii))
    #print(abs(float(focenc.ascii)-float(foctarg.ascii)))
    #print(float(foctarg.ascii))
    
    #time.sleep(2)

    if abs(float(focenc.ascii)-float(foctarg.ascii)) < float(foctol.ascii) and move==True:
        say("Focus: Target equals current, no move needed.")
        return focmm.ascii

    # check if move is possible
    checkIfMoveIsPossible(focstatus)

    # if move is True, then force a move
    if move==True:
        focmove.write(1)

        # move expressions
        moving        = '$kbms.focmove == 1'
        not_moving    = '$kbms.focmove == 0'

        moving = ktl.Expression(moving)
        not_moving = ktl.Expression(not_moving)

        # wait for moving
        result = moving.wait(timeout = timeOutMove)
        if not quiet:
            p = AnimatedProgressBar(end=100, width=standardWidth)
            ktl.monitor(server,'FOCPROG',ProgressCallback,p)

        if result == False:
            raise RuntimeError("Mechanism %s did not start moving within %d seconds" % ("Blue camera focus", timeOutMove))

        # wait for not moving
        not_moving.wait(timeout=timeOutComplete)

        # check for successful move
        time.sleep(4)
        checkSuccess(statusKeyword=focstatus, mechanism="Blue Camera focus", targetReachedExpression=None, successStatus="OK")
        if abs(focposerr) > foctol:
            say("Warning: The required focus has NOT been reached")

        # return value
        result = focmm.ascii
        lg.info("kcwiServer: Returning blue camera focus '%s'" % (result))
        return result