예제 #1
0
class Elmo(Controller):
    """
    Elmo motor controller

    configuration example:
    - class: elmo
      udp:
      url: nscopeelmo
      axes:
        - name: rot
          steps_per_unit: 26222.2
          velocity: 377600
          acceleration: 755200
          control_slave: True
    """
    ErrorCode = {
        1: 'Do not Update',
        2: 'Bad Command',
        3: 'Bad Index',
        5: 'BEYOND_ALPHA_BET (Has No Interpreter Meaning',
        6: 'Program is not running',
        7: 'Mode cannot be started - bad initialization data',
        8: 'Motion terminated, probably data underflow',
        9: 'CAN message was lost',
        10: 'Cannot be used by PDO',
        11: 'Cannot write to flash memory',
        12: 'Command not available in this unit mode',
        13: 'Cannot reset communication - UART is busy',
        14: 'Cannot perform CAN NMT service',
        15: 'CAN Life time exceeded - motor shut down',
        16: "The command attribute is array '[]' is expected",
        17: 'Format of UL command is not valid - check the command definition',
        18: 'Empty Assign',
        19: 'Command syntax error',
        21: 'Operand Out of Range',
        22: 'Zero Division',
        23: 'Command cannot be assigned',
        24: 'Bad Operation',
        25: 'Command Not Valid While Moving',
        26: 'Profiler mode not supported in this unit mode (UM)',
        28: 'Out Of Limit',
        29: 'CAN set object return an abort when called from interpreter',
        30: 'No program to continue',
        31: 'CAN get object return an abort when called from interpreter',
        32: 'Communication overrun, parity, noise, or framing error',
        33: 'Bad sensor setting',
        34: 'There is a conflict with another command',
        36:
        'Commutation method (CA[17]) or commutation table does not fit to sensor',
        37: 'Two Or More Hall sensors are defined to the same place',
        38: 'Motion start specified for the past',
        41: 'Command is not supported by this product',
        42: 'No Such Label',
        43: 'CAN state machine in fault(object 0x6041 in DS-402)',
        45: 'Return Error From Subroutine',
        46: 'May Not Use Multi- capture Homing Mode With Stop Event',
        47: 'Program does not exist or not Compiled',
        48: 'Motor cold not start - fault reason in CD',
        50: 'Stack Overflow',
        51: 'Inhibit OR Abort inputs are active, Cannot start motor',
        52: 'PVT Queue Full',
        53: 'Only For Current',
        54: 'Bad Data Base',
        55: 'Bad Context',
        56: 'The product grade does not support this command',
        57: 'Motor Must be Off',
        58: 'Motor Must be On',
        60: 'Bad Unit Mode',
        61: 'Data Base Reset',
        64: 'Cannot set the index of an active table',
        65: 'Disabled By SW',
        66: 'Amplifier Not Ready',
        67: 'Recorder Is Busy',
        68: 'Required profiler mode is not supported',
        69: 'Recorder Usage Error',
        70: 'Recorder data Invalid',
        71: 'Homing is busy',
        72: 'Modulo range must be even',
        73: 'Please Set Position',
        74: 'Bad profile database, see 0x2081 for object number (EE[2])',
        77: 'Buffer Too Large',
        78: 'Out of Program Range',
        80: 'ECAM data inconsistent',
        81: 'Download failed see specific error in EE[3]',
        82: 'Program Is Running',
        83: 'Command is not permitted in a program.',
        84: 'The System Is Not In Point To Point Mode',
        86: 'PVT table is soon going to underflow',
        88: 'ECAM last interval is larger than allowed',
        90: 'CAN state machine not ready (object 0x6041 in DS-402)',
        91: 'Bad PVT Head Pointer',
        92: 'PDO not configured',
        93: 'There is a wrong initiation value for this command',
        95: 'ER[3] Too large for modulo setting applied',
        96: 'User program time out',
        97: 'RS232 receive buffer overflow',
        98: 'Cannot measure current offsets',
        99: 'Bad auxiliary sensor configuration',
        100: 'The requested PWM value is not supported',
        101: 'Absolute encoder setting problem',
        105: 'Speed loop KP out of range',
        106: 'Position loop KP out of range',
        110: 'Too long number',
        111: 'KV vector is invalid',
        112: 'KV defines scheduled block but scheduling is off',
        113: 'Exp task queue is full',
        114: 'Exp task queue is empty',
        115: 'Exp output queue is full',
        116: 'Exp output queue is empty',
        117: 'Bad KV setting for sensor filter',
        118: 'Bad KV setting',
        119: 'Analog Sensor filter out of range',
        120: 'Analog Sensor filter may contain 0 or 2 blocks',
        121: 'Please wait until Analog Sensor initialized',
        122: 'Motion mode is not supported or with initialization conflict',
        123: 'Profiler queue is full',
        125: 'Personality not loaded',
        126: 'User Program failed - variable out of program size',
        127: 'Modulo range must be positive',
        128: 'Bad variable index in database',
        129: 'Variable is not an array',
        130: 'Variable name does not exist',
        131: 'Cannot record local variable',
        132: 'Variable is an array',
        133: 'Number of function input arguments is not as expected',
        134: 'Cannot run local label/function with the XQ command',
        135: 'Frequency identification failed',
        136: 'Not a number',
        137: 'Program already compiled',
        139: 'The number of break points exceeds maximal number',
        140: 'An attempt to set/clear break point at the not relevant line',
        141: 'Boot Identity parameters section is not clear',
        142: 'Checksum of data is not correct',
        143: 'Missing boot identity parameters',
        144: 'Numeric Stack underflow',
        145: 'Numeric stack overflow',
        146: 'Expression stack overflow',
        147: 'Executable command within math expression',
        148: 'Nothing in the expression',
        149: 'Unexpected sentence termination',
        150: 'Sentence terminator not found',
        151: 'Parentheses mismatch',
        152: 'Bad operand type',
        153: 'Overflow in a numeric operator',
        154: 'Address is out of data memory segment',
        155: 'Beyond stack range',
        156: 'Bad op-code',
        157: 'No Available program stack',
        158: 'Out of flash memory range',
        159: 'Flash memory verification error',
        160: 'Program aborted by another thread',
        161: 'Program is not halted.',
        162: 'Badly formatted number.',
        163:
        'There is not enough space in the program data segment. Try to reduce variable usage in the user program.',
        164: 'EC command (not an error)',
        165: 'An attempt was made to access serial flash memory while busy.',
        166: 'Out of modulo range.',
        167: 'Infinite loop in for loop - zero step',
        168: 'Speed too large to start motor.',
        169: 'Time out using peripheral.(overflo w or busy)',
        170: 'Cannot erase sector in flash memory',
        171: 'Cannot read from flash memory',
        172: 'Cannot write to flash memory',
        173: 'Executable area of program is too large',
        174: 'Program has not been loaded',
        175: 'Cannot write program checksum - clear program (CP)',
        176: 'User code, variables and functions are too large',
        181: 'Writing to Flash program area, failed',
        182: 'PAL Burn Is In Process or no PAL is burned',
        183: 'PAL Burn (PB Command) Is Disabled',
        184: 'Capture option already used by other operation',
        185:
        'This element may be modified only when interpolation is not active',
        186: 'Interpolation queue is full',
        187: 'Incorrect Interpolation sub-mode',
        188: 'Gantry slave is disabled',
    }

    def __init__(self, *args, **kwargs):
        Controller.__init__(self, *args, **kwargs)
        self._cnx = None

    def initialize(self):
        config = self.config.config_dict
        if get_comm_type(config) == UDP:
            opt = {'port': 5001, 'eol': ';'}
        else:  # SERIAL
            opt = {'baudrate': 115200, 'eol': ';'}

        self._cnx = get_comm(config, **opt)
        self._elmostate = AxisState()
        for state, human in (('SLAVESWITCH', 'Slave switch activate'),
                             ('INHIBITSWITCH', 'Inhibit switch active'),
                             ('CLOSEDLOOPOPEN', 'Closed loop open'),
                             ('DRIVEFAULT', 'Problem into the drive')):
            self._elmostate.create_state(state, human)

    def initialize_hardware(self):
        # Check that the controller is alive
        try:
            self._query("VR\r", timeout=50e-3)
        except SocketTimeout:
            raise RuntimeError("Controller Elmo (%s) is not connected" % \
                               (self._cnx._host))

    def initialize_axis(self, axis):
        axis._mode = Cache(axis, "mode", default_value=None)

    def initialize_hardware_axis(self, axis):
        # Check user-mode
        mode = int(self._query("UM"))
        asked_mode = axis.config.get('user_mode', int, 5)
        if (mode != asked_mode):
            self._query("UM=%d" % asked_mode)
        # Check closed loop on
        if self._query("MO") != '1':
            self.set_on(axis)
        mode = self._query("UM")
        axis._mode.value = int(mode)

    def set_on(self, axis):
        self._set_power(axis, True)

    def set_off(self, axis):
        self._set_power(axis, False)

    def _set_power(self, axis, activate):
        # Activate slave if needed
        if axis.config.get('control_slave', bool, False):
            self._query("OB[1]=%d" % activate)

        self._query("MO=%d" % activate)

    def _query(self, msg, in_error_code=False, **keys):
        send_message = msg + '\r'
        raw_reply = self._cnx.write_readline(send_message, **keys)
        if not raw_reply.startswith(send_message):  # something weird happened
            self._cnx.close()
            raise RuntimeError("received reply: %s\n"
                               "expected message starts with %s" % msg)
        reply = raw_reply[len(send_message):]
        if not in_error_code and reply.endswith('?'):
            error_code = self._query("EC", in_error_code=True)
            try:
                error_code = int(error_code)
            except ValueError:  # Weird, don't know what to do
                pass
            else:
                human_error = self.ErrorCode.get(error_code, 'Unknown')
                raise RuntimeError("Error %d (%s), Query (%s)" %
                                   (error_code, human_error, msg))
        return reply

    def start_jog(self, axis, velocity, direction):
        self._query("JV=%d" % velocity * direction)
        self._query("BG")

    def stop_jog(self, axis):
        self._query("JV=0")
        self._query("BG")
        #check if sync_hard needed

    def read_position(self, axis):
        if axis._mode == 2:
            return float(self._query("PX"))
        else:
            return float(self._query("DV[3]"))

    def set_position(self, axis, new_pos):
        pos = round(new_pos)
        self._set_power(axis, False)
        encodeur_name = "PY" if axis._mode == 4 else "PX"
        self._query("%s=%d" % (encodeur_name, pos))
        self._set_power(axis, True)
        self._query("PA=%d" % pos)
        return self.read_position(axis)

    def read_acceleration(self, axis):
        return int(self._query("AC"))

    def set_acceleration(self, axis, new_acc):
        self._query("AC=%d" % new_acc)
        self._query("DC=%d" % new_acc)
        return self.read_acceleration(axis)

    def read_velocity(self, axis):
        return float(self._query("SP"))

    def set_velocity(self, axis, new_vel):
        self._query("SP=%d" % new_vel)
        return self.read_velocity(axis)

    def home_search(self, axis, switch, set_pos=None):
        #can't set the position when searching home
        #we should change emotion to add an option in
        #home search i.e: set_pos = None
        if set_pos is not None:
            commands = [
                "HM[3]=3",
                "HM[2]=%d" % round(set_pos), "HM[4]=0", "HM[5]=0", "HM[1]=1"
            ]
        else:
            commands = ["HM[3]=3", "HM[4]=0", "HM[5]=2", "HM[1]=1"]
        for cmd in commands:
            self._query(cmd)
        #Start search
        step_sign = 1 if axis.config.get('steps_per_unit', float) > 0 else -1
        self._query("JV=%d", switch * step_sign * self.read_velocity())
        self._query("BG")

    def state(self, axis):
        state = self._elmostate.new()

        # check first that the controller is ready to move
        # bit0 of Status Register (page 3.135)
        ans = int(self._query("SR"))
        if (ans & (1 << 7)):
            state.set("MOVING")
        if (ans & 0x1):  # problem into the drive
            state.set("DRIVEFAULT")
        if not (ans & (1 << 4)):  # closed loop open
            state.set("CLOSEDLOOPOPEN")

        # Check limits
        ans = int(self._query("IP"))
        if axis.config.get('control_slave', bool, False):
            if ans & (1 << 17):
                state.set("SLAVESWITCH")

        if ans & (1 << 6):
            state.set("LIMPOS")
        if ans & (1 << 7):
            state.set("LIMNEG")
        if ans & (1 << 8):
            #should be checked in spec ends in MOT_EMERGENCY ?
            state.set("INHIBITSWITCH")

        # Check motion state
        # Wrong if homing
        #
        ans = int(self._query("MS"))
        if ans == 0:
            state.set("READY")
        elif ans == 1 or ans == 2:
            state.set("MOVING")
        elif ans == 3:
            state.set("FAULT")

        return state

    def start_one(self, motion):
        # check first that the controller is ready to move
        # bit0 of Status Register (page 3.135)
        ans = int(self._query("SR"))
        if (ans & 0x1):
            raise RuntimeError("problem into the drive")
        if not (ans & (1 << 4)):
            raise RuntimeError("closed loop open")

        self._query("PA=%d" % round(motion.target_pos))
        self._query("BG")

    def stop(self, axis):
        self._query("ST")
        #todo spec macros check if motor is in homing phase...

    @object_method(types_info=("None", "int"))
    def get_user_mode(self, axis):
        return int(self._query("UM"))

    @object_method(types_info=("int", "int"))
    def set_user_mode(self, axis, mode):
        commands = ["MO=0", "UM=%d" % mode]
        if mode == 2:
            commands.append("PM=1")
        commands.append("MO=1")
        for cmd in commands:
            self._query(cmd)
        if mode == 5 or mode == 4:
            self.sync_hard()
        mode = int(self._query("UM"))
        axis._mode.value = mode
        return mode

    @object_method(types_info=("None", ("float", "float")))
    def jog_range(self, axis):
        #this method should be in emotion
        #todo move it has a generic
        return float(self._query("VL[2]")), float(self._query("VH[2]"))

    @object_method(types_info=("None", "bool"))
    def get_enable_slave(self, axis):
        return bool(self._query("OB[1]"))

    @object_method(types_info=("bool", "bool"))
    def set_enable_slave(self, axis, active):
        self._query("OB[1]=%d" % active)
        return bool(self._query("OB[1]"))

    #encoders
    def initialize_encoder(self, encoder):
        pass

    def read_encoder(self, encoder):
        return float(self._query("PX"))

    def set_encoder(self, encoder, steps):
        self._query("PX=%d" % steps)
예제 #2
0
파일: elmo.py 프로젝트: tiagocoutinho/bliss
class Elmo(Controller):
    """
    Elmo motor controller

    configuration example:
    - class: elmo
      udp:
      url: nscopeelmo
      axes:
        - name: rot
          steps_per_unit: 26222.2
          velocity: 377600
          acceleration: 755200
          control_slave: True
    """
    ErrorCode = {
        1 : 'Do not Update',
        2 : 'Bad Command',
        3 : 'Bad Index',
        5 : 'BEYOND_ALPHA_BET (Has No Interpreter Meaning',
        6 : 'Program is not running',
        7 : 'Mode cannot be started - bad initialization data',
        8 : 'Motion terminated, probably data underflow',
        9 : 'CAN message was lost',
        10 : 'Cannot be used by PDO',
        11 : 'Cannot write to flash memory',
        12 : 'Command not available in this unit mode',
        13 : 'Cannot reset communication - UART is busy',
        14 : 'Cannot perform CAN NMT service',
        15 : 'CAN Life time exceeded - motor shut down',
        16 : "The command attribute is array '[]' is expected",
        17 : 'Format of UL command is not valid - check the command definition',
        18 : 'Empty Assign',
        19 : 'Command syntax error',
        21 : 'Operand Out of Range',
        22 : 'Zero Division',
        23 : 'Command cannot be assigned',
        24 : 'Bad Operation',
        25 : 'Command Not Valid While Moving',
        26 : 'Profiler mode not supported in this unit mode (UM)',
        28 : 'Out Of Limit',
        29 : 'CAN set object return an abort when called from interpreter',
        30 : 'No program to continue',
        31 : 'CAN get object return an abort when called from interpreter',
        32 : 'Communication overrun, parity, noise, or framing error',
        33 : 'Bad sensor setting',
        34 : 'There is a conflict with another command',
        36 : 'Commutation method (CA[17]) or commutation table does not fit to sensor',
        37 : 'Two Or More Hall sensors are defined to the same place',
        38 : 'Motion start specified for the past',
        41 : 'Command is not supported by this product',
        42 : 'No Such Label',
        43 : 'CAN state machine in fault(object 0x6041 in DS-402)',
        45 : 'Return Error From Subroutine',
        46 : 'May Not Use Multi- capture Homing Mode With Stop Event',
        47 : 'Program does not exist or not Compiled',
        48 : 'Motor cold not start - fault reason in CD',
        50 : 'Stack Overflow',
        51 : 'Inhibit OR Abort inputs are active, Cannot start motor',
        52 : 'PVT Queue Full',
        53 : 'Only For Current',
        54 : 'Bad Data Base',
        55 : 'Bad Context',
        56 : 'The product grade does not support this command',
        57 : 'Motor Must be Off',
        58 : 'Motor Must be On',
        60 : 'Bad Unit Mode',
        61 : 'Data Base Reset',
        64 : 'Cannot set the index of an active table',
        65 : 'Disabled By SW',
        66 : 'Amplifier Not Ready',
        67 : 'Recorder Is Busy',
        68 : 'Required profiler mode is not supported',
        69 : 'Recorder Usage Error',
        70 : 'Recorder data Invalid',
        71 : 'Homing is busy',
        72 : 'Modulo range must be even',
        73 : 'Please Set Position',
        74 : 'Bad profile database, see 0x2081 for object number (EE[2])',
        77 : 'Buffer Too Large',
        78 : 'Out of Program Range',
        80 : 'ECAM data inconsistent',
        81 : 'Download failed see specific error in EE[3]',
        82 : 'Program Is Running',
        83 : 'Command is not permitted in a program.',
        84 : 'The System Is Not In Point To Point Mode',
        86 : 'PVT table is soon going to underflow',
        88 : 'ECAM last interval is larger than allowed',
        90 : 'CAN state machine not ready (object 0x6041 in DS-402)',
        91 : 'Bad PVT Head Pointer',
        92 : 'PDO not configured',
        93 : 'There is a wrong initiation value for this command',
        95 : 'ER[3] Too large for modulo setting applied',
        96 : 'User program time out',
        97 : 'RS232 receive buffer overflow',
        98 : 'Cannot measure current offsets',
        99 : 'Bad auxiliary sensor configuration',
        100 : 'The requested PWM value is not supported',
        101 : 'Absolute encoder setting problem',
        105 : 'Speed loop KP out of range',
        106 : 'Position loop KP out of range',
        110 : 'Too long number',
        111 : 'KV vector is invalid',
        112 : 'KV defines scheduled block but scheduling is off',
        113 : 'Exp task queue is full',
        114 : 'Exp task queue is empty',
        115 : 'Exp output queue is full',
        116 : 'Exp output queue is empty',
        117 : 'Bad KV setting for sensor filter',
        118 : 'Bad KV setting',
        119 : 'Analog Sensor filter out of range',
        120 : 'Analog Sensor filter may contain 0 or 2 blocks',
        121 : 'Please wait until Analog Sensor initialized',
        122 : 'Motion mode is not supported or with initialization conflict',
        123 : 'Profiler queue is full',
        125 : 'Personality not loaded',
        126 : 'User Program failed - variable out of program size',
        127 : 'Modulo range must be positive',
        128 : 'Bad variable index in database',
        129 : 'Variable is not an array',
        130 : 'Variable name does not exist',
        131 : 'Cannot record local variable',
        132 : 'Variable is an array',
        133 : 'Number of function input arguments is not as expected',
        134 : 'Cannot run local label/function with the XQ command',
        135 : 'Frequency identification failed',
        136 : 'Not a number',
        137 : 'Program already compiled',
        139 : 'The number of break points exceeds maximal number',
        140 : 'An attempt to set/clear break point at the not relevant line',
        141 : 'Boot Identity parameters section is not clear',
        142 : 'Checksum of data is not correct',
        143 : 'Missing boot identity parameters',
        144 : 'Numeric Stack underflow',
        145 : 'Numeric stack overflow',
        146 : 'Expression stack overflow',
        147 : 'Executable command within math expression',
        148 : 'Nothing in the expression',
        149 : 'Unexpected sentence termination',
        150 : 'Sentence terminator not found',
        151 : 'Parentheses mismatch',
        152 : 'Bad operand type',
        153 : 'Overflow in a numeric operator',
        154 : 'Address is out of data memory segment',
        155 : 'Beyond stack range',
        156 : 'Bad op-code',
        157 : 'No Available program stack',
        158 : 'Out of flash memory range',
        159 : 'Flash memory verification error',
        160 : 'Program aborted by another thread',
        161 : 'Program is not halted.',
        162 : 'Badly formatted number.',
        163 : 'There is not enough space in the program data segment. Try to reduce variable usage in the user program.',
        164 : 'EC command (not an error)',
        165 : 'An attempt was made to access serial flash memory while busy.',
        166 : 'Out of modulo range.',
        167 : 'Infinite loop in for loop - zero step',
        168 : 'Speed too large to start motor.',
        169 : 'Time out using peripheral.(overflo w or busy)',
        170 : 'Cannot erase sector in flash memory',
        171 : 'Cannot read from flash memory',
        172 : 'Cannot write to flash memory',
        173 : 'Executable area of program is too large',
        174 : 'Program has not been loaded',
        175 : 'Cannot write program checksum - clear program (CP)',
        176 : 'User code, variables and functions are too large',
        181 : 'Writing to Flash program area, failed',
        182 : 'PAL Burn Is In Process or no PAL is burned',
        183 : 'PAL Burn (PB Command) Is Disabled',
        184 : 'Capture option already used by other operation',
        185 : 'This element may be modified only when interpolation is not active',
        186 : 'Interpolation queue is full',
        187 : 'Incorrect Interpolation sub-mode',
        188 : 'Gantry slave is disabled',
        }
    
    def __init__(self,*args,**kwargs):
        Controller.__init__(self,*args,**kwargs)
        self._cnx = None
        
    def initialize(self):
        config = self.config.config_dict
        if get_comm_type(config) == UDP:
            opt = {'port':5001,'eol':';'}
        else:                   # SERIAL
            opt = {'baudrate':115200,'eol':';'}
            
        self._cnx = get_comm(config,**opt)
        self._elmostate = AxisState()
        for state,human in (('SLAVESWITCH','Slave switch activate'),
                            ('INHIBITSWITCH','Inhibit switch active'),
                            ('CLOSEDLOOPOPEN','Closed loop open'),
                            ('DRIVEFAULT','Problem into the drive')):
            self._elmostate.create_state(state,human)
            
    def initialize_hardware(self):
        # Check that the controller is alive
        try:
            self._query("VR\r",timeout=50e-3)
        except SocketTimeout:
            raise RuntimeError("Controller Elmo (%s) is not connected" % \
                               (self._cnx._host))

    def initialize_axis(self,axis):
        axis._mode = Cache(axis,"mode",default_value = None)
    
    def initialize_hardware_axis(self, axis):
        # Check user-mode
        mode= int(self._query("UM"))
        asked_mode = axis.config.get('user_mode',int,5)
        if (mode != asked_mode):
            self._query("UM=%d" % asked_mode)
        # Check closed loop on
        if self._query("MO") != '1':
            self.set_on(axis)
        mode= self._query("UM")
        axis._mode.value = int(mode)
        
    def set_on(self, axis):
        self._set_power(axis,True)

    def set_off(self, axis):
        self._set_power(axis,False)

    def _set_power(self, axis, activate):
        # Activate slave if needed
        if axis.config.get('control_slave',bool,False):
            self._query("OB[1]=%d" % activate)

        self._query("MO=%d" % activate)
        
    def _query(self,msg,in_error_code = False,**keys):
        send_message = msg + '\r'
        raw_reply = self._cnx.write_readline(send_message,**keys)
        if not raw_reply.startswith(send_message): # something weird happened
            self._cnx.close()
            raise RuntimeError("received reply: %s\n"
                               "expected message starts with %s" % msg)
        reply = raw_reply[len(send_message):]
        if not in_error_code and reply.endswith('?'):
            error_code = self._query("EC",in_error_code = True)
            try:
                error_code = int(error_code)
            except ValueError:  # Weird, don't know what to do
                pass
            else:
                human_error = self.ErrorCode.get(error_code,'Unknown')
                raise RuntimeError("Error %d (%s), Query (%s)" %
                                   (error_code,human_error,msg))
        return reply

    def start_jog(self,axis,velocity,direction):
        self._query("JV=%d" % velocity * direction)
        self._query("BG")

    def stop_jog(self,axis):
        self._query("JV=0")
        self._query("BG")
        #check if sync_hard needed

    def read_position(self,axis):
        if axis._mode == 2:
            return float(self._query("PX"))
        else:
            return float(self._query("DV[3]"))

    def set_position(self,axis,new_pos):
        pos = round(new_pos)
        self._set_power(axis,False)
        encodeur_name = "PY" if axis._mode == 4 else "PX"
        self._query("%s=%d" % (encodeur_name,pos))
        self._set_power(axis,True)
        self._query("PA=%d" % pos)
        return self.read_position(axis)
    
    def read_acceleration(self,axis):
        return int(self._query("AC"))

    def set_acceleration(self,axis,new_acc):
        self._query("AC=%d" % new_acc)
        self._query("DC=%d" % new_acc)
        return self.read_acceleration(axis)
    
    def read_velocity(self,axis):
        return float(self._query("SP"))

    def set_velocity(self,axis,new_vel):
        self._query("SP=%d" % new_vel)
        return self.read_velocity(axis)

    def home_search(self,axis,switch,set_pos = None):
        #can't set the position when searching home
        #we should change emotion to add an option in
        #home search i.e: set_pos = None
        if set_pos is not None:
            commands = ["HM[3]=3","HM[2]=%d" % round(set_pos),
                        "HM[4]=0","HM[5]=0","HM[1]=1"]
        else:
            commands = ["HM[3]=3","HM[4]=0","HM[5]=2","HM[1]=1"]
        for cmd in commands:
            self._query(cmd)
        #Start search
        step_sign = 1 if axis.config.get('steps_per_unit',float) > 0 else -1
        self._query("JV=%d", switch * step_sign * self.read_velocity())
        self._query("BG")
        
    def state(self,axis):
        state = self._elmostate.new()
        
        # check first that the controller is ready to move
        # bit0 of Status Register (page 3.135)
        ans = int(self._query("SR"))
        if(ans & (1<<7)):
            state.set("MOVING")
        if(ans & 0x1):          # problem into the drive
            state.set("DRIVEFAULT")
        if not (ans & (1<<4)):    # closed loop open
            state.set("CLOSEDLOOPOPEN")

        # Check limits
        ans = int(self._query("IP"))
        if axis.config.get('control_slave',bool,False):
            if ans & (1<<17):
                state.set("SLAVESWITCH")

        if ans & (1<<6):
            state.set("LIMPOS")
        if ans & (1<<7):
            state.set("LIMNEG")
        if ans & (1<<8):
            #should be checked in spec ends in MOT_EMERGENCY ?
            state.set("INHIBITSWITCH")

        # Check motion state
        # Wrong if homing
        # 
        ans = int(self._query("MS"))
        if ans == 0:
            state.set("READY")
        elif ans == 1 or ans == 2:
            state.set("MOVING")
        elif ans == 3:
            state.set("FAULT")

        return state
    
    def start_one(self,motion):
        # check first that the controller is ready to move
        # bit0 of Status Register (page 3.135)
        ans = int(self._query("SR"))
        if(ans & 0x1):
            raise RuntimeError("problem into the drive")
        if not (ans & (1<<4)):
            raise RuntimeError("closed loop open")

        self._query("PA=%d" % round(motion.target_pos))
        self._query("BG")

    def stop(self,axis):
        self._query("ST")
        #todo spec macros check if motor is in homing phase...

    @object_method(types_info=("None","int"))
    def get_user_mode(self,axis):
        return int(self._query("UM"))

    @object_method(types_info=("int","int"))
    def set_user_mode(self,axis,mode):
        commands = ["MO=0","UM=%d" % mode]
        if mode == 2:
            commands.append("PM=1")
        commands.append("MO=1")
        for cmd in commands:
            self._query(cmd)
        if mode == 5 or mode == 4:
            self.sync_hard()
        mode = int(self._query("UM"))
        axis._mode.value = mode
        return mode

    @object_method(types_info=("None",("float","float")))
    def jog_range(self,axis):
        #this method should be in emotion
        #todo move it has a generic
        return float(self._query("VL[2]")),float(self._query("VH[2]"))

    @object_method(types_info=("None","bool"))
    def get_enable_slave(self,axis):
        return bool(self._query("OB[1]"))

    @object_method(types_info=("bool","bool"))
    def set_enable_slave(self,axis,active):
        self._query("OB[1]=%d" % active)
        return bool(self._query("OB[1]"))
    
    #encoders
    def initialize_encoder(self,encoder):
        pass

    def read_encoder(self,encoder):
        return float(self._query("PX"))

    def set_encoder(self,encoder,steps):
        self._query("PX=%d" % steps)
예제 #3
0
class Icepap(Controller):
    """
    IcePAP stepper controller without Deep Technology of Communication.
    But if you prefer to have it (DTC) move to IcePAP controller class.
    Use this class controller at your own risk, because you won't
    have any support...
    """
    STATUS_DISCODE = {
        0 : ('POWERENA',      'power enabled'),                   
        1 : ('NOTACTIVE',     'axis configured as not active'),   
        2 : ('ALARM',         'alarm condition'),                 
        3 : ('REMRACKDIS',    'remote rack disable input signal'),
        4 : ('LOCRACKDIS',    'local rack disable switch'),       
        5 : ('REMAXISDIS',    'remote axis disable input signal'),
        6 : ('LOCAXISDIS',    'local axis disable switch'),       
        7 : ('SOFTDIS',       'software disable'),
    }

    STATUS_MODCODE = {
        0 : ('OPER',          'operation mode'),     
        1 : ('PROG',          'programmation mode'), 
        2 : ('TEST',          'test mode'),          
        3 : ('FAIL',          'fail mode'),
    }
    STATUS_STOPCODE = {
        0 : ('SCEOM',         'end of movement'),                 
        1 : ('SCSTOP',        'last motion was stopped'),         
        2 : ('SCABORT',       'last motion was aborted'),         
        3 : ('SCLIMPOS',      'positive limitswitch reached'),    
        4 : ('SCLINNEG',      'negative limitswitch reached'),    
        5 : ('SCSETTLINGTO',  'settling timeout'),                
        6 : ('SCAXISDIS',     'axis disabled (no alarm)'),        
        7 : ('SCBIT7',        'n/a'),                             
        8 : ('SCINTFAIL',     'internal failure'),                
        9 : ('SCMOTFAIL',     'motor failure'),                   
        10 : ('SCPOWEROVL',   'power overload'),                  
        11 : ('SCHEATOVL',    'driver overheating'),              
        12 : ('SCCLERROR',    'closed loop error'),               
        13 : ('SCCENCERROR',  'control encoder error'),           
        14 : ('SCBIT14',      'n/a'),                             
        15 : ('SCEXTALARM',   'external alarm'),
    }

    def __init__(self,*args,**kwargs):
        Controller.__init__(self,*args,**kwargs)
        self._cnx = None
        self._last_axis_power_time = dict()

    def initialize(self):
        hostname = self.config.get("host")
        self._cnx = Command(hostname,5000,eol='\n')

        self._icestate = AxisState()
        self._icestate.create_state("POWEROFF", "motor power is off")
        for codes in (self.STATUS_DISCODE,self.STATUS_MODCODE,self.STATUS_STOPCODE):
            for state,desc in codes.values():
                self._icestate.create_state(state,desc)

    def finalize(self):
        if self._cnx is not None:
            self._cnx.close()
            
    def initialize_axis(self,axis):
        axis.address = axis.config.get("address",lambda x: x)

        if hasattr(axis,'_init_software'):
            axis._init_software()

    def initialize_hardware_axis(self,axis):
        if axis.config.get('autopower', converter=bool, default=True):
            try:
                self.set_on(axis)
            except:
                sys.excepthook(*sys.exc_info())

        if hasattr(axis,'_init_hardware'):
            axis._init_hardware()

    #Axis power management 
    def set_on(self,axis):
        """
        Put the axis power on
        """
        self._power(axis,True)

    def set_off(self,axis):
        """
        Put the axis power off
        """
        self._power(axis,False)

    def _power(self,axis,power):
        _ackcommand(self._cnx,"POWER %s %s" % 
                    ("ON" if power else "OFF",axis.address))
        self._last_axis_power_time[axis] = time.time()

    def read_position(self,axis,cache=True):
        pos_cmd = "FPOS" if cache else "POS"
        return int(_command(self._cnx,"?%s %s" % (pos_cmd,axis.address)))
    
    def set_position(self,axis,new_pos):
        if isinstance(axis,SlaveAxis):
            pre_cmd = "%d:DISPROT LINKED;" % axis.address
        else:
            pre_cmd = None
        _ackcommand(self._cnx,"POS %s %d" % (axis.address,int(round(new_pos))),
                    pre_cmd = pre_cmd)
        return self.read_position(axis,cache=False)

    def read_velocity(self,axis):
        return float(_command(self._cnx,"?VELOCITY %s" % axis.address))

    def set_velocity(self,axis,new_velocity):
        _ackcommand(self._cnx,"VELOCITY %s %f" % 
                    (axis.address,new_velocity))
        return self.read_velocity(axis)

    def read_acceleration(self,axis):
        acctime = float(_command(self._cnx,"?ACCTIME %s" % axis.address))
        velocity = self.read_velocity(axis)
        return velocity/float(acctime)

    def set_acceleration(self,axis,new_acc):
        velocity = self.read_velocity(axis)
        new_acctime = velocity/new_acc

        _ackcommand(self._cnx,"ACCTIME %s %f" % (axis.address,new_acctime))
        return self.read_acceleration(axis)

    def state(self,axis):
        last_power_time = self._last_axis_power_time.get(axis,0)
        if time.time() - last_power_time < 1.:
            status_cmd = "?STATUS"
        else:
            self._last_axis_power_time.pop(axis,None)
            status_cmd = "?FSTATUS"

        status = int(_command(self._cnx,"%s %s" %
                              (status_cmd,axis.address)),16)
        status ^= 1<<23 #neg POWERON FLAG
        state = self._icestate.new()
        for mask,value in (((1<<9),"READY"),
                           ((1<<10|1<<11),"MOVING"),
                           ((1<<18),"LIMPOS"),
                           ((1<<19),"LIMNEG"),
                           ((1<<20),"HOME"),
                           ((1<<23),"POWEROFF")):
            if status & mask:
                state.set(value)

        state_mode = (status >> 2) & 0x3
        if state_mode:
            state.set(self.STATUS_MODCODE.get(state_mode)[0])

        stop_code = (status >> 14) & 0xf
        if stop_code:
            state.set(self.STATUS_STOPCODE.get(stop_code)[0])

        disable_condition = (status >> 4) & 0x7
        if disable_condition:
            state.set(self.STATUS_DISCODE.get(disable_condition)[0])

        if state.READY:
            #if motor is ready then no need to investigate deeper
            return state

        if not state.MOVING:
            # it seems it is not safe to call warning and/or alarm commands
            # while homing motor, so let's not ask if motor is moving
            if status & (1<<13):
                try:
                    warning = _command(self._cnx,"%d:?WARNING" % axis.address)
                except TypeError:
                    pass
                else:
                    warn_str =  "warning condition: \n" + warning
                    status.create_state("WARNING",warn_str)
                    status.set("WARNING")

            try:
                alarm = _command(self._cnx,"%d:?ALARM" % axis.address)
            except (RuntimeError,TypeError):
                pass
            else:
                if alarm != "NO":
                    alarm_dsc = "alarm condition: " + str(alarm)
                    state.create_state("ALARMDESC",alarm_dsc)
                    state.set("ALARMDESC")

        return state

    def get_info(self,axis):
        pre_cmd = '%s:' % axis.address
        r =  "MOTOR   : %s\n" % axis.name
        r += "SYSTEM  : %s (ID: %s) (VER: %s)\n" % (self._cnx._host,
                                                    _command(self._cnx,"0:?ID"),
                                                    _command(self._cnx,"?VER"))
        r += "DRIVER  : %s\n" % axis.address
        r += "POWER   : %s\n" % _command(self._cnx,pre_cmd + "?POWER")
        r += "CLOOP   : %s\n" % _command(self._cnx,pre_cmd + "?PCLOOP")
        r += "WARNING : %s\n" % _command(self._cnx,pre_cmd + "?WARNING")
        r += "ALARM   : %s\n" % _command(self._cnx,pre_cmd + "?ALARM")
        return r
    
    def raw_write(self,message,data = None):
        return _command(self._cnx,message,data)
        
    def raw_write_read(self,message,data = None):
        return _ackcommand(self._cnx,message,data)

    def prepare_move(self,motion):
        pass

    def start_one(self,motion):
        if isinstance(motion.axis,SlaveAxis):
            pre_cmd = "%d:DISPROT LINKED;" % motion.axis.address
        else:
            pre_cmd = None

        _ackcommand(self._cnx,"MOVE %s %d" % (motion.axis.address,
                                              motion.target_pos),
                    pre_cmd = pre_cmd)

    def start_all(self,*motions):
        if motions > 1:
            cmd = "MOVE GROUP "
            cmd += ' '.join(["%s %d" % (m.axis.address,m.target_pos) for m in motions])
            _ackcommand(self._cnx,cmd)
        elif motions:
            self.start_one(motions[0])

    def stop(self,axis):
        _command(self._cnx,"STOP %s" % axis.address)

    def stop_all(self,*motions):
        for motion in motions:
            self.stop(motion.axis)

    def home_search(self,axis,switch):
        cmd = "HOME " + ("+1" if switch > 0 else "-1")
        _ackcommand(self._cnx,"%s:%s" % (axis.address,cmd))
        # IcePAP status is not immediately MOVING after home search command is sent
        gevent.sleep(0.2)

    def home_state(self,axis):
        s = self.state(axis)
        if s != 'READY' and s != 'POWEROFF':
             s.set('MOVING')
        return s

    def limit_search(self,axis,limit):
        cmd = "SRCH LIM" + ("+" if limit>0 else "-")
        _ackcommand(self._cnx,"%s:%s" % (axis.address,cmd))
        # TODO: MG18Nov14: remove this sleep (state is not immediately MOVING)
        gevent.sleep(0.1)

    def initialize_encoder(self,encoder):
        # Get axis config from bliss config
        # address form is XY : X=rack {0..?} Y=driver {1..8}
        encoder.address = encoder.config.get("address", int)

        # Get optional encoder input to read
        enctype = encoder.config.get("type",str,"ENCIN").upper()
        # Minium check on encoder input
        if enctype not in ['ENCIN', 'ABSENC', 'INPOS', 'MOTOR', 'AXIS', 'SYNC']:
            raise ValueError('Invalid encoder type')
        encoder.enctype = enctype

    def read_encoder(self,encoder):
        value = _command(self._cnx,"?ENC %s %d" % (encoder.enctype,encoder.address))
        return int(value)

    def set_encoder(self,encoder,steps):
        _ackcommand(self._cnx,"ENC %s %d %d" % 
                    (encoder.enctype,encoder.address,steps))

    def set_event_positions(self,axis_or_encoder,positions):
        int_position = numpy.array(positions,dtype=numpy.int32)
        #position has to be ordered
        int_position.sort()
        address = axis_or_encoder.address
        if not len(int_position):
            _ackcommand(self._cnx,"%s:ECAMDAT CLEAR" % address)
            return

        if isinstance(axis_or_encoder,Axis):
            source = 'AXIS'
        else:                   # encoder
            source = 'MEASURE'

        #load trigger positions
        _ackcommand(self._cnx,"%s:*ECAMDAT %s DWORD" % (address,source),
                    int_position)
         # send the trigger on the multiplexer
        _ackcommand(self._cnx,"%s:SYNCAUX eCAM" % address)

    def get_event_positions(self,axis_or_encoder):
        """
        For this controller this method should be use
        for debugging purposed only... 
        """
        address = axis_or_encoder.address
        #Get the number of positions
        reply = _command(self._cnx,"%d:?ECAMDAT" % address)
        reply_exp = re.compile("(\w+) +([+-]?\d+) +([+-]?\d+) +(\d+)")
        m = reply_exp.match(reply)
        if m is None:
            raise RuntimeError("Reply Didn't expected: %s" % reply)
        source = m.group(1)
        nb = int(m.group(4))
        
        if isinstance(axis_or_encoder,Axis):
            nb = nb if source == 'AXIS' else 0
        else:                   # encoder
            nb = nb if source == "MEASURE" else 0

        positions = numpy.zeros((nb,),dtype = numpy.int32)
        if nb > 0:
            reply_exp = re.compile(".+: +([+-]?\d+)")
            reply = _command(self._cnx,"%d:?ECAMDAT %d" % (address,nb))
            for i,line in enumerate(reply.split('\n')):
                m = reply_exp.match(line)
                if m:
                    pos = int(m.group(1))
                    positions[i] = pos
        return positions

    def get_linked_axis(self):
        reply = _command(self._cnx,"?LINKED")
        linked = dict()
        for line in reply.strip().split('\n'):
            values = line.split()
            linked[values[0]] = [int(x) for x in values[1:]]
        return linked

    @object_method(types_info=("bool","bool"))
    def activate_closed_loop(self,axis,active):
        _command(self._cnx,"#%s:PCLOOP %s" % (axis.address,"ON" if active else "OFF"))
        return active

    @object_method(types_info=("None","bool"))
    def is_closed_loop_activate(self,axis):
        return True if _command(self._cnx,"%s:?PCLOOP" % axis.address) == 'ON' else False

    @object_method(types_info=("None","None"))
    def reset_closed_loop(self,axis):
        measure_position = int(_command(self._cnx,"%s:?POS MEASURE" % axis.address))
        self.set_position(axis,measure_position)
        if axis.config.get('autopower', converter=bool, default=True):
            self.set_on(axis)
        axis.sync_hard()
        
    @object_method(types_info=("None","int"))
    def temperature(self,axis):
        return int(_command(self._cnx,"%s:?MEAS T" % axis.address))

    @object_method(types_info=(("float","bool"),"None"))
    def set_tracking_positions(self,axis,positions,cyclic = False):
        """
        Send position to the controller which will be tracked.

        positions --  are expressed in user unit
        cyclic -- cyclic position or not default False

        @see activate_track method
        """
        address = axis.address
        if not len(positions):
            _ackcommand(self._cnx,"%s:LISTDAT CLEAR" % address)
            return

        dial_positions = axis.user2dial(numpy.array(positions, dtype=numpy.float))
        step_positions = numpy.array(dial_positions * axis.steps_per_unit,
                                     dtype=numpy.int32)
        _ackcommand(self._cnx,"%d:*LISTDAT %s DWORD" % 
                    (address, "CYCLIC" if cyclic else "NOCYCLIC"),
                    step_positions)

    @object_method(types_info=("None",("float","bool")))
    def get_tracking_positions(self,axis):
        """
        Get the tacking positions.
        This method should only be use for debugging
        return a tuple with (positions,cyclic flag)
        """
        address = axis.address
        #Get the number of positions
        reply = _command(self._cnx,"%d:?LISTDAT" % address)
        reply_exp = re.compile("(\d+) *(\w+)?")
        m = reply_exp.match(reply)
        if m is None:
            raise RuntimeError("Reply didn't expected: %s" % reply)
        nb = int(m.group(1))
        positions = numpy.zeros((nb,),dtype = numpy.int32)
        cyclic = True if m.group(2) == "CYCLIC" else False
        if nb > 0:
            reply_exp = re.compile(".+: +([+-]?\d+)")
            reply = _command(self._cnx,"%d:?LISTDAT %d" % (address,nb))
            for i,line in enumerate(reply.split('\n')):
                m = reply_exp.match(line)
                if m:
                    pos = int(m.group(1))
                    positions[i] = pos
            dial_positions = positions / axis.steps_per_unit
            positions = axis.dial2user(dial_positions)
        return positions,cyclic

    @object_method(types_info=(("bool","str"),"None"))
    def activate_tracking(self,axis,activate,mode = None):
        """
        Activate/Deactivate the tracking position depending on
        activate flag
        mode -- default "INPOS" if None.
        mode can be :
           - SYNC   -> Internal SYNC signal
           - ENCIN  -> ENCIN signal
           - INPOS  -> INPOS signal
           - ABSENC -> ABSENC signal
        """
        address = axis.address

        if not activate:
            _ackcommand(self._cnx,"STOP %d" % address)
            axis.sync_hard()
        else:
            if mode is None: mode = "INPOS"
            possibles_modes = ["SYNC","ENCIN","INPOS","ABSENC"]
            if mode not in possibles_modes:
                raise ValueError("mode %s is not managed, can only choose %s" % 
                                 (mode,possibles_modes))
            if mode == "INPOS":
                _ackcommand(self._cnx, "%d:POS INPOS 0" % address)
            _ackcommand(self._cnx,"%d:LTRACK %s" % (address,mode))
        
    @object_method(types_info=("float", "None"))
    def blink(self, axis, second=3.):
        """
        Blink axis driver
        """
        _command(self._cnx,"%d:BLINK %f" % (axis.address, second))

    def reset(self):
        _command(self._cnx,"RESET")

    def mdspreset(self):
        """
        Reset the MASTER DSP
        """
        _command(self._cnx,"_dsprst")

    def reboot(self):
        _command(self._cnx,"REBOOT")
        self._cnx.close()
예제 #4
0
class Icepap(Controller):
    """
    IcePAP stepper controller without Deep Technology of Communication.
    But if you prefer to have it (DTC) move to IcePAP controller class.
    Use this class controller at your own risk, because you won't
    have any support...
    """
    STATUS_DISCODE = {
        0: ('POWERENA', 'power enabled'),
        1: ('NOTACTIVE', 'axis configured as not active'),
        2: ('ALARM', 'alarm condition'),
        3: ('REMRACKDIS', 'remote rack disable input signal'),
        4: ('LOCRACKDIS', 'local rack disable switch'),
        5: ('REMAXISDIS', 'remote axis disable input signal'),
        6: ('LOCAXISDIS', 'local axis disable switch'),
        7: ('SOFTDIS', 'software disable'),
    }

    STATUS_MODCODE = {
        0: ('OPER', 'operation mode'),
        1: ('PROG', 'programmation mode'),
        2: ('TEST', 'test mode'),
        3: ('FAIL', 'fail mode'),
    }
    STATUS_STOPCODE = {
        0: ('SCEOM', 'end of movement'),
        1: ('SCSTOP', 'last motion was stopped'),
        2: ('SCABORT', 'last motion was aborted'),
        3: ('SCLIMPOS', 'positive limitswitch reached'),
        4: ('SCLINNEG', 'negative limitswitch reached'),
        5: ('SCSETTLINGTO', 'settling timeout'),
        6: ('SCAXISDIS', 'axis disabled (no alarm)'),
        7: ('SCBIT7', 'n/a'),
        8: ('SCINTFAIL', 'internal failure'),
        9: ('SCMOTFAIL', 'motor failure'),
        10: ('SCPOWEROVL', 'power overload'),
        11: ('SCHEATOVL', 'driver overheating'),
        12: ('SCCLERROR', 'closed loop error'),
        13: ('SCCENCERROR', 'control encoder error'),
        14: ('SCBIT14', 'n/a'),
        15: ('SCEXTALARM', 'external alarm'),
    }

    def __init__(self, *args, **kwargs):
        Controller.__init__(self, *args, **kwargs)
        self._cnx = None
        self._last_axis_power_time = dict()

    def initialize(self):
        hostname = self.config.get("host")
        self._cnx = Command(hostname, 5000, eol='\n')

        self._icestate = AxisState()
        self._icestate.create_state("POWEROFF", "motor power is off")
        for codes in (self.STATUS_DISCODE, self.STATUS_MODCODE,
                      self.STATUS_STOPCODE):
            for state, desc in codes.values():
                self._icestate.create_state(state, desc)

    def finalize(self):
        if self._cnx is not None:
            self._cnx.close()

    def initialize_axis(self, axis):
        axis.address = axis.config.get("address", lambda x: x)

        if hasattr(axis, '_init_software'):
            axis._init_software()

    def initialize_hardware_axis(self, axis):
        if axis.config.get('autopower', converter=bool, default=True):
            try:
                self.set_on(axis)
            except:
                sys.excepthook(*sys.exc_info())

        if hasattr(axis, '_init_hardware'):
            axis._init_hardware()

    #Axis power management
    def set_on(self, axis):
        """
        Put the axis power on
        """
        self._power(axis, True)

    def set_off(self, axis):
        """
        Put the axis power off
        """
        self._power(axis, False)

    def _power(self, axis, power):
        _ackcommand(self._cnx,
                    "POWER %s %s" % ("ON" if power else "OFF", axis.address))
        self._last_axis_power_time[axis] = time.time()

    def read_position(self, axis, cache=True):
        pos_cmd = "FPOS" if cache else "POS"
        return int(_command(self._cnx, "?%s %s" % (pos_cmd, axis.address)))

    def set_position(self, axis, new_pos):
        if isinstance(axis, SlaveAxis):
            pre_cmd = "%d:DISPROT LINKED;" % axis.address
        else:
            pre_cmd = None
        _ackcommand(self._cnx,
                    "POS %s %d" % (axis.address, int(round(new_pos))),
                    pre_cmd=pre_cmd)
        return self.read_position(axis, cache=False)

    def read_velocity(self, axis):
        return float(_command(self._cnx, "?VELOCITY %s" % axis.address))

    def set_velocity(self, axis, new_velocity):
        _ackcommand(self._cnx, "VELOCITY %s %f" % (axis.address, new_velocity))
        return self.read_velocity(axis)

    def read_acceleration(self, axis):
        acctime = float(_command(self._cnx, "?ACCTIME %s" % axis.address))
        velocity = self.read_velocity(axis)
        return velocity / float(acctime)

    def set_acceleration(self, axis, new_acc):
        velocity = self.read_velocity(axis)
        new_acctime = velocity / new_acc

        _ackcommand(self._cnx, "ACCTIME %s %f" % (axis.address, new_acctime))
        return self.read_acceleration(axis)

    def state(self, axis):
        last_power_time = self._last_axis_power_time.get(axis, 0)
        if time.time() - last_power_time < 1.:
            status_cmd = "?STATUS"
        else:
            self._last_axis_power_time.pop(axis, None)
            status_cmd = "?FSTATUS"

        status = int(_command(self._cnx, "%s %s" % (status_cmd, axis.address)),
                     16)
        status ^= 1 << 23  #neg POWERON FLAG
        state = self._icestate.new()
        for mask, value in (((1 << 9), "READY"), ((1 << 10 | 1 << 11),
                                                  "MOVING"),
                            ((1 << 18), "LIMPOS"), ((1 << 19), "LIMNEG"),
                            ((1 << 20), "HOME"), ((1 << 23), "POWEROFF")):
            if status & mask:
                state.set(value)

        state_mode = (status >> 2) & 0x3
        if state_mode:
            state.set(self.STATUS_MODCODE.get(state_mode)[0])

        stop_code = (status >> 14) & 0xf
        if stop_code:
            state.set(self.STATUS_STOPCODE.get(stop_code)[0])

        disable_condition = (status >> 4) & 0x7
        if disable_condition:
            state.set(self.STATUS_DISCODE.get(disable_condition)[0])

        if state.READY:
            #if motor is ready then no need to investigate deeper
            return state

        if not state.MOVING:
            # it seems it is not safe to call warning and/or alarm commands
            # while homing motor, so let's not ask if motor is moving
            if status & (1 << 13):
                try:
                    warning = _command(self._cnx, "%d:?WARNING" % axis.address)
                except TypeError:
                    pass
                else:
                    warn_str = "warning condition: \n" + warning
                    status.create_state("WARNING", warn_str)
                    status.set("WARNING")

            try:
                alarm = _command(self._cnx, "%d:?ALARM" % axis.address)
            except (RuntimeError, TypeError):
                pass
            else:
                if alarm != "NO":
                    alarm_dsc = "alarm condition: " + str(alarm)
                    state.create_state("ALARMDESC", alarm_dsc)
                    state.set("ALARMDESC")

        return state

    def get_info(self, axis):
        pre_cmd = '%s:' % axis.address
        r = "MOTOR   : %s\n" % axis.name
        r += "SYSTEM  : %s (ID: %s) (VER: %s)\n" % (
            self._cnx._host, _command(self._cnx,
                                      "0:?ID"), _command(self._cnx, "?VER"))
        r += "DRIVER  : %s\n" % axis.address
        r += "POWER   : %s\n" % _command(self._cnx, pre_cmd + "?POWER")
        r += "CLOOP   : %s\n" % _command(self._cnx, pre_cmd + "?PCLOOP")
        r += "WARNING : %s\n" % _command(self._cnx, pre_cmd + "?WARNING")
        r += "ALARM   : %s\n" % _command(self._cnx, pre_cmd + "?ALARM")
        return r

    def raw_write(self, message, data=None):
        return _command(self._cnx, message, data)

    def raw_write_read(self, message, data=None):
        return _ackcommand(self._cnx, message, data)

    def prepare_move(self, motion):
        pass

    def start_one(self, motion):
        if isinstance(motion.axis, SlaveAxis):
            pre_cmd = "%d:DISPROT LINKED;" % motion.axis.address
        else:
            pre_cmd = None

        _ackcommand(self._cnx,
                    "MOVE %s %d" % (motion.axis.address, motion.target_pos),
                    pre_cmd=pre_cmd)

    def start_all(self, *motions):
        if motions > 1:
            cmd = "MOVE GROUP "
            cmd += ' '.join(
                ["%s %d" % (m.axis.address, m.target_pos) for m in motions])
            _ackcommand(self._cnx, cmd)
        elif motions:
            self.start_one(motions[0])

    def stop(self, axis):
        _command(self._cnx, "STOP %s" % axis.address)

    def stop_all(self, *motions):
        for motion in motions:
            self.stop(motion.axis)

    def home_search(self, axis, switch):
        cmd = "HOME " + ("+1" if switch > 0 else "-1")
        _ackcommand(self._cnx, "%s:%s" % (axis.address, cmd))
        # IcePAP status is not immediately MOVING after home search command is sent
        gevent.sleep(0.2)

    def home_state(self, axis):
        s = self.state(axis)
        if s != 'READY' and s != 'POWEROFF':
            s.set('MOVING')
        return s

    def limit_search(self, axis, limit):
        cmd = "SRCH LIM" + ("+" if limit > 0 else "-")
        _ackcommand(self._cnx, "%s:%s" % (axis.address, cmd))
        # TODO: MG18Nov14: remove this sleep (state is not immediately MOVING)
        gevent.sleep(0.1)

    def initialize_encoder(self, encoder):
        # Get axis config from bliss config
        # address form is XY : X=rack {0..?} Y=driver {1..8}
        encoder.address = encoder.config.get("address", int)

        # Get optional encoder input to read
        enctype = encoder.config.get("type", str, "ENCIN").upper()
        # Minium check on encoder input
        if enctype not in [
                'ENCIN', 'ABSENC', 'INPOS', 'MOTOR', 'AXIS', 'SYNC'
        ]:
            raise ValueError('Invalid encoder type')
        encoder.enctype = enctype

    def read_encoder(self, encoder):
        value = _command(self._cnx,
                         "?ENC %s %d" % (encoder.enctype, encoder.address))
        return int(value)

    def set_encoder(self, encoder, steps):
        _ackcommand(self._cnx,
                    "ENC %s %d %d" % (encoder.enctype, encoder.address, steps))

    def set_event_positions(self, axis_or_encoder, positions):
        int_position = numpy.array(positions, dtype=numpy.int32)
        #position has to be ordered
        int_position.sort()
        address = axis_or_encoder.address
        if not len(int_position):
            _ackcommand(self._cnx, "%s:ECAMDAT CLEAR" % address)
            return

        if isinstance(axis_or_encoder, Axis):
            source = 'AXIS'
        else:  # encoder
            source = 'MEASURE'

        #load trigger positions
        _ackcommand(self._cnx, "%s:*ECAMDAT %s DWORD" % (address, source),
                    int_position)
        # send the trigger on the multiplexer
        _ackcommand(self._cnx, "%s:SYNCAUX eCAM" % address)

    def get_event_positions(self, axis_or_encoder):
        """
        For this controller this method should be use
        for debugging purposed only... 
        """
        address = axis_or_encoder.address
        #Get the number of positions
        reply = _command(self._cnx, "%d:?ECAMDAT" % address)
        reply_exp = re.compile("(\w+) +([+-]?\d+) +([+-]?\d+) +(\d+)")
        m = reply_exp.match(reply)
        if m is None:
            raise RuntimeError("Reply Didn't expected: %s" % reply)
        source = m.group(1)
        nb = int(m.group(4))

        if isinstance(axis_or_encoder, Axis):
            nb = nb if source == 'AXIS' else 0
        else:  # encoder
            nb = nb if source == "MEASURE" else 0

        positions = numpy.zeros((nb, ), dtype=numpy.int32)
        if nb > 0:
            reply_exp = re.compile(".+: +([+-]?\d+)")
            reply = _command(self._cnx, "%d:?ECAMDAT %d" % (address, nb))
            for i, line in enumerate(reply.split('\n')):
                m = reply_exp.match(line)
                if m:
                    pos = int(m.group(1))
                    positions[i] = pos
        return positions

    def get_linked_axis(self):
        reply = _command(self._cnx, "?LINKED")
        linked = dict()
        for line in reply.strip().split('\n'):
            values = line.split()
            linked[values[0]] = [int(x) for x in values[1:]]
        return linked

    @object_method(types_info=("bool", "bool"))
    def activate_closed_loop(self, axis, active):
        _command(self._cnx,
                 "#%s:PCLOOP %s" % (axis.address, "ON" if active else "OFF"))
        return active

    @object_method(types_info=("None", "bool"))
    def is_closed_loop_activate(self, axis):
        return True if _command(self._cnx, "%s:?PCLOOP" %
                                axis.address) == 'ON' else False

    @object_method(types_info=("None", "None"))
    def reset_closed_loop(self, axis):
        measure_position = int(
            _command(self._cnx, "%s:?POS MEASURE" % axis.address))
        self.set_position(axis, measure_position)
        if axis.config.get('autopower', converter=bool, default=True):
            self.set_on(axis)
        axis.sync_hard()

    @object_method(types_info=("None", "int"))
    def temperature(self, axis):
        return int(_command(self._cnx, "%s:?MEAS T" % axis.address))

    @object_method(types_info=(("float", "bool"), "None"))
    def set_tracking_positions(self, axis, positions, cyclic=False):
        """
        Send position to the controller which will be tracked.

        positions --  are expressed in user unit
        cyclic -- cyclic position or not default False

        @see activate_track method
        """
        address = axis.address
        if not len(positions):
            _ackcommand(self._cnx, "%s:LISTDAT CLEAR" % address)
            return

        dial_positions = axis.user2dial(
            numpy.array(positions, dtype=numpy.float))
        step_positions = numpy.array(dial_positions * axis.steps_per_unit,
                                     dtype=numpy.int32)
        _ackcommand(
            self._cnx, "%d:*LISTDAT %s DWORD" %
            (address, "CYCLIC" if cyclic else "NOCYCLIC"), step_positions)

    @object_method(types_info=("None", ("float", "bool")))
    def get_tracking_positions(self, axis):
        """
        Get the tacking positions.
        This method should only be use for debugging
        return a tuple with (positions,cyclic flag)
        """
        address = axis.address
        #Get the number of positions
        reply = _command(self._cnx, "%d:?LISTDAT" % address)
        reply_exp = re.compile("(\d+) *(\w+)?")
        m = reply_exp.match(reply)
        if m is None:
            raise RuntimeError("Reply didn't expected: %s" % reply)
        nb = int(m.group(1))
        positions = numpy.zeros((nb, ), dtype=numpy.int32)
        cyclic = True if m.group(2) == "CYCLIC" else False
        if nb > 0:
            reply_exp = re.compile(".+: +([+-]?\d+)")
            reply = _command(self._cnx, "%d:?LISTDAT %d" % (address, nb))
            for i, line in enumerate(reply.split('\n')):
                m = reply_exp.match(line)
                if m:
                    pos = int(m.group(1))
                    positions[i] = pos
            dial_positions = positions / axis.steps_per_unit
            positions = axis.dial2user(dial_positions)
        return positions, cyclic

    @object_method(types_info=(("bool", "str"), "None"))
    def activate_tracking(self, axis, activate, mode=None):
        """
        Activate/Deactivate the tracking position depending on
        activate flag
        mode -- default "INPOS" if None.
        mode can be :
           - SYNC   -> Internal SYNC signal
           - ENCIN  -> ENCIN signal
           - INPOS  -> INPOS signal
           - ABSENC -> ABSENC signal
        """
        address = axis.address

        if not activate:
            _ackcommand(self._cnx, "STOP %d" % address)
            axis.sync_hard()
        else:
            if mode is None: mode = "INPOS"
            possibles_modes = ["SYNC", "ENCIN", "INPOS", "ABSENC"]
            if mode not in possibles_modes:
                raise ValueError("mode %s is not managed, can only choose %s" %
                                 (mode, possibles_modes))
            if mode == "INPOS":
                _ackcommand(self._cnx, "%d:POS INPOS 0" % address)
            _ackcommand(self._cnx, "%d:LTRACK %s" % (address, mode))

    @object_method(types_info=("float", "None"))
    def blink(self, axis, second=3.):
        """
        Blink axis driver
        """
        _command(self._cnx, "%d:BLINK %f" % (axis.address, second))

    def reset(self):
        _command(self._cnx, "RESET")

    def mdspreset(self):
        """
        Reset the MASTER DSP
        """
        _command(self._cnx, "_dsprst")

    def reboot(self):
        _command(self._cnx, "REBOOT")
        self._cnx.close()