import ConfigParserRT import cortex import logging import time class mechanism: """ Base clase for instrument mechanisms. Stores mechanism status information and provides methods corresponding to basic mechanism actions which dispacth the appropriate sequence of commands to a cortex instance. """ def __init__(self, cortex, icu_num, axis_num, \ base_speed, max_speed, acceleration, deceleration, \ position, name='mechanism'): """ Initialiser for mechanism class. Arguments: cortex: The cortex based ICU daisy chain instance that this mechanism is a part of. icuNum: Which ICU in the chain this mechanism is connected to. axisNum: Which axis of the ICU the mechansism is connected to. baseSpeed: Starting speed (steps/half steps per sec?) maxSpeed: Peak speed (steps/half steps per sec?) acceleration:Acceleration (steps/half steps per sec^2?) deceleration:Deceleration (steps/half steps per sec^2?) position: Initial position of the mechanism (steps) name: Label used for logging purposes. """ # Store basic 'identity' of the mechanism self.name = name self.cortex = cortex self.icuNum = int(icu_num) self.axisNum = int(axis_num) # Store motor parameters self.baseSpeed = int(base_speed) self.maxSpeed = int(max_speed) self.acceleration = int(acceleration) self.deceleration = int(deceleration) # Status of the mechanism self.position = int(position) # Set up logging self.logger = logging.getLogger(name) # Say something self.logger.info('Assigned to ICU %i, axis %i of %s' \ % (self.icuNum, self.axisNum, self.cortex.name)) def updateConfig(self, config): """ Should be called by the parent instrument object's destructor to enable status information to be saved to file on exit. Takes a ConfigParserRT.SafeConfigParser object as argument. """ self.logger.debug("updateConfig() called.") if isinstance(config, ConfigParserRT.SafeConfigParser): config.set(self.name, 'base_speed', str(self.baseSpeed)) config.set(self.name, 'max_speed', str(self.maxSpeed)) config.set(self.name, 'acceleration', str(self.acceleration)) config.set(self.name, 'deceleration', str(self.deceleration)) config.set(self.name, 'position', str(self.position)) else: self.logger.critical("Invalid argument to updateConfig(%s)" \ % repr(config)) self.logger.critical("Unable to save status data") def getMotorParams(self): """ Returns a tuple of the four motor motion parameters, baseSpeed, maxSpeed, acceleration, deceleration. """ params = (self.baseSpeed, self.maxSpeed, self.acceleration, \ self.deceleration) self.logger.debug('getMotorParams() called') return params def getPosition(self): """ Returns the current motor position. """ self.logger.debug('getPosition() called') return self.position def moveBy(self, steps, blocking=True): """ Initiates a movement of the mechanism by a given number of steps. Second, optional, boolean argument specifies whether the call should block until the movement has been completed or return immediately (default is True). """ self.logger.debug('moveBy(%i, %i) called' % (steps, blocking)) # First of all check that the ICU isn't busy. if self.cortex.isActive(self.icuNum): self.logger.critical(\ 'Attempt to move mechanism when ICU busy, ignored.') return # Activate the relevant axis self.cortex.setOutput(self.axisNum, self.icuNum) # Check the limit switches. If we're trying to move in the direction # of a made limit then refuse to move at all. If it appears we're # moving away from a limit log a warning but do it anyway. # NB If both limits are made the cortex card will return an arbitrary # result. test = steps * self.cortex.getLimitStatus(self.icuNum) if test > 0: self.logger.critical(\ 'Attempt to move in direction of a made limit, ignored') return if test < 0: self.logger.warning('Moving away from a made limit') # Set the move going. self.logger.debug('Moving %i steps' % steps) self.cortex.setMotorParams(self.baseSpeed, self.maxSpeed, \ self.acceleration, self.deceleration, self.icuNum) self.cortex.setDatum(self.position, self.icuNum) self.cortex.move(steps, self.icuNum) if blocking: # Wait for an (under)estimate of the time it'll take to # complete the move. time.sleep(abs(steps)/self.maxSpeed) # Periodically quiz the ICU to see if the move has finished. while self.cortex.isActive(self.icuNum): time.sleep(1) # Check we didn't hit the limits during the move. limit_status = self.cortex.getLimitStatus(self.icuNum) if limit_status: self.logger.warning('Limit switch made in direction %i' % \ limit_status) # Can now disable the axis self.cortex.setOutput(7, self.icuNum) # Get final position from ICU to update internal record # May add some extra sanity checking here. self.position = self.cortex.getPosition(self.icuNum) else: self.logger.critical('Non-blocking move not implemented!') self.cortex.setOutput(7, self.icuNum) # TODO: Fire off a worker thread to monitor the move before # returning. def moveTo(self, new_position, blocking=True): self.logger.debug('moveTo(%i, %i) called' % (new_position, blocking)) self.moveBy((new_position - self.position), blocking) def setMotorParams(self, baseSpeed, maxSpeed, acceleration, deceleration): """ Sets the four motor motion parameters, baseSpeed, maxSpeed, acceleration and deceleration. Takes 4 integer arguments. """ self.logger.debug('setMotorParams(%i, %i, %i, %i) called' % \ (baseSpeed, maxSpeed, acceleration, deceleration)) # TODO: Need some sanity checking of the numbers here, really... self.baseSpeed = baseSpeed self.maxSpeed = maxSpeed self.acceleration = acceleration self.deceleration = deceleration def setPosition(self, new_position=0): """ Defines the current mechanism position to be equal to the single integer argument. If no argument is given, the mechanism position is zeroed. """ self.logger.debug('setPosition(%i) called' % new_position) # TODO: When non-blocking moves are implemented will want to # prevent this being done during a move, or confusion # will result. self.position = int(new_position)