void AMControlMoveAction::onMoveStarted() { disconnect(control_, SIGNAL(moveStarted()), this, SLOT(onMoveStarted())); disconnect(control_, SIGNAL(moveReTargetted()), this, SLOT(onMoveStarted())); // From now on, if we get a moveReTargetted() signal, this would be bad: someone is re-directing our move to a different setpoint, so this now counts as a failure. connect(control_, SIGNAL(moveReTargetted()), this, SLOT(onMoveReTargetted())); notifyStarted(); // start the progressTick timer connect(&progressTick_, SIGNAL(timeout()), this, SLOT(onProgressTick())); progressTick_.start(250); }
void AMControlMoveAction::startImplementation() { const AMControlInfo& setpoint = *(controlMoveInfo()->controlInfo()); // lookup the control we need to use to run this control_ = getControlForAction(setpoint); // Must have a control, and it must be able to move. if(!control_) { AMErrorMon::report(AMErrorReport(this, AMErrorReport::Alert, -1, QString("There was an error moving the control '%1' into position, because the control was not found. Please report this problem to the Acquaman developers.").arg(setpoint.name()))); notifyFailed(); return; } // check we can move... if(!control_->canMove()) { AMErrorMon::report(AMErrorReport(this, AMErrorReport::Alert, -2, QString("There was an error moving the control '%1' into position, because the control was not connected and ready to move. Please report this problem to the beamline staff.") .arg(control_->name()))); notifyFailed(); return; } // check that the destination is in range... if(control_->valueOutOfRange(controlMoveInfo()->isRelativeMove() ? control_->value()+setpoint.value() : setpoint.value())) { AMErrorMon::report(AMErrorReport(this, AMErrorReport::Alert, -3, QString("There was an error moving the control '%1' into position, because the destination %2 %3 was outside its range. Please report this problem to the beamline staff.") .arg(control_->name()) .arg(setpoint.value()) .arg(setpoint.units()))); notifyFailed(); return; } // connect to its moveSucceeded and moveFailed signals connect(control_, SIGNAL(moveStarted()), this, SLOT(onMoveStarted())); connect(control_, SIGNAL(moveReTargetted()), this, SLOT(onMoveStarted())); // For controls that support allowsMovesWhileMoving(), they might already be moving when we request our move(). A moveReTargetted() signal from them also counts as a moveStarted() for us. connect(control_, SIGNAL(moveFailed(int)), this, SLOT(onMoveFailed(int))); connect(control_, SIGNAL(moveSucceeded()), this, SLOT(onMoveSucceeded())); // remember the start position: startPosition_ = control_->toInfo(); // start the move: int failureExplanation; if(controlMoveInfo()->isRelativeMove()) failureExplanation = control_->moveRelative(setpoint.value()); else failureExplanation = control_->move(setpoint.value()); if(failureExplanation != AMControl::NoFailure) onMoveFailed(failureExplanation); }
// Start a move to the value setpoint: AMControl::FailureExplanation CLSMDriveMotorControl::move(double Setpoint) { if(isMoving()) { if(!allowsMovesWhileMoving()) { AMErrorMon::debug(this, AMPVCONTROL_COULD_NOT_MOVE_WHILE_MOVING, QString("AMPVControl: Could not move %1 (%2) to %3, because the control is already moving.").arg(name()).arg(writePV_->pvName()).arg(setpoint_)); return AlreadyMovingFailure; } if(!moveInProgress()) { // the control is already moving, but it's not one of our moves. In this situation, there is no way that we can start a move and be assured that we'll be notified when OUR move finishes. AMErrorMon::debug(this, AMPVCONTROL_COULD_NOT_MOVE_WHILE_MOVING_EXTERNAL, QString("AMPVControl: Could not move %1 (%2) to %3, because the control is already moving.").arg(name()).arg(writePV_->pvName()).arg(setpoint_)); return AlreadyMovingFailure; } // Otherwise: This control supports mid-move updates, and we're already moving. We just need to update the setpoint and send it. setpoint_ = writeUnitConverter()->convertToRaw(Setpoint); writePV_->setValue(setpoint_); // since the settling phase is considered part of a move, it's OK to be here while settling... But for Acquaman purposes, this will be considered a single re-targetted move, even though the hardware will see two. If we're settling, disable the settling timer, because we only want to respond to the end of the second move. if(settlingInProgress_) { settlingInProgress_ = false; settlingTimer_.stop(); } emit moveReTargetted(); // re-targetted moves will emit moveReTargetted(), although no moveSucceeded()/moveFailed() will be issued for the initial move. } else { settlingInProgress_ = false; stopInProgress_ = false; moveInProgress_ = false; // Flag that "our" move started: startInProgress_ = true; // This is our new target: setpoint_ = writeUnitConverter()->convertToRaw(Setpoint); // Special case: "null move" should complete immediately. Use only if moveStartTolerance() is non-zero, and the move distance is within moveStartTolerance(). if(moveStartTolerance() != 0 && fabs(setpoint()-value()) < moveStartTolerance()) { startInProgress_ = false; moveInProgress_ = true; emit moveStarted(); moveInProgress_ = false; emit moveSucceeded(); } // Normal move: else { // Issue the move command: writePV_->setValue(setpoint_); // start the timer to check if our move failed to start: moveStartTimer_.start(int(moveStartTimeout_*1000.0)); } } return NoFailure; }
// Start a move to the value setpoint: AMControl::FailureExplanation AMPVControl::move(double setpoint) { if(isMoving()) { if(!allowsMovesWhileMoving()) { AMErrorMon::debug(this, AMPVCONTROL_COULD_NOT_MOVE_WHILE_MOVING, QString("AMPVControl: Could not move %1 (%2) to %3, because the control is already moving.").arg(name()).arg(writePV_->pvName()).arg(setpoint_)); return AlreadyMovingFailure; } // assuming this control can accept mid-move updates. We just need to update our setpoint and send it. if(!canMove()) { // this would be rare: a past move worked, but now we're no longer connected? AMErrorMon::debug(this, AMPVCONTROL_COULD_NOT_MOVE_BASED_ON_CANMOVE, QString("AMPVControl: Could not move %1 (%2) to %3.").arg(name()).arg(writePV_->pvName()).arg(setpoint_)); return NotConnectedFailure; } setpoint_ = setpoint; writePV_->setValue(setpoint_); completionTimer_.start(int(completionTimeout_*1000.0)); // restart the completion timer... Since this might be another move, give it some more time. // re-targetted moves will emit moveReTargetted(), although no moveSucceeded()/moveFailed() will be issued for the first move. emit moveReTargetted(); // check for done: if(inPosition()) { completionTimer_.stop(); emit movingChanged(moveInProgress_ = false); emit moveSucceeded(); } } // Regular case: start of a new move. else { // kill any old countdowns: completionTimer_.stop(); if(!canMove()) { AMErrorMon::debug(this, AMPVCONTROL_COULD_NOT_MOVE_BASED_ON_CANMOVE, QString("AMPVControl: Could not move %1 (%2) to %3.").arg(name()).arg(writePV_->pvName()).arg(setpoint_)); return NotConnectedFailure; } // new move target: setpoint_ = setpoint; // Issue the move, check on attemptMoveWhenWithinTolerance if(!attemptMoveWhenWithinTolerance_ && inPosition()){ emit moveSucceeded(); } else{ writePV_->setValue(setpoint_); // We're now moving! Let's hope this control makes it... (No way to actually check.) emit movingChanged(moveInProgress_ = true); // emit the signal that we started: emit moveStarted(); // Are we in-position? [With the default tolerance of AMCONTROL_TOLERANCE_DONT_CARE, we will always be in-position, and moves will complete right away, that's the intended behaviour, because we have no other way of knowing when they'll finish.] if(inPosition()) { emit movingChanged(moveInProgress_ = false); emit moveSucceeded(); } else { // start the countdown to see if we get there in time or stall out: (completionTimeout_ is in seconds) completionTimer_.start(int(completionTimeout_*1000.0)); } } } return NoFailure; }