void AMCompositeControl::onChildControlMovingChanged() { bool nowMoving = isMoving(); if(wasMoving_ && !nowMoving) emit movingChanged(wasMoving_ = nowMoving); if(!wasMoving_ && nowMoving) emit movingChanged(wasMoving_ = nowMoving); }
void AMPVwStatusControl::onSettlingTimeFinished() { settlingTimer_.stop(); if(!settlingInProgress_) { // temporary, for settling state testing: qWarning() << "AMPVwStatusControl:" << name() << ": Settling timeout while settlingInProgress is not true. This should never happen; please report this bug."; return; } settlingInProgress_ = false; if(moveInProgress_) { moveInProgress_ = false; // Check if we succeeded... if(inPosition()) { emit moveSucceeded(); } else { emit moveFailed(AMControl::ToleranceFailure); } } else { // temporary, for settling state testing: qWarning() << "AMPVwStatusControl: " << name() << ": Settling time reached while moveInProgress_ == false. This should never happen; please report this bug."; } // possible that a change in settlingInProgress_ caused isMoving() to change. Emit signal if necessary: bool nowMoving = isMoving(); if(nowMoving != wasMoving_) emit movingChanged(wasMoving_ = nowMoving); }
// This is called whenever there is an update from the move status PV // In it we simply do change detection of the moving status, and emit signals if it has changed. void AMReadOnlyPVwStatusControl::onMovingChanged(int movingValue) { bool nowMoving = (*statusChecker_)(movingValue); if(wasMoving_ != nowMoving) emit movingChanged(wasMoving_ = nowMoving); }
// This is used to check every new value, to see if we entered tolerance: void AMPVControl::onNewFeedbackValue(double) { // If we're not in the middle of a move, don't really care about changing values. if(!moveInProgress_) return; // Did we make it? if( inPosition() ) { // move is now done: emit movingChanged(moveInProgress_ = false); // disable the timer, so it doesn't trigger an error later completionTimer_.stop(); // let everyone know we succeeded: emit moveSucceeded(); } }
// This is used to handle the timeout of a move: void AMPVControl::onCompletionTimeout() { // if we weren't moving, this shouldn't have happened. someone forgot to shutoff the timer? // todo: this is only included for state testing debugging... can remove if never happens if(!moveInProgress_) { return; } // No matter what, this move is over: emit movingChanged(moveInProgress_ = false); completionTimer_.stop(); // Did we make it? if( inPosition() ) { emit moveSucceeded(); } // Didn't make it into position: else { emit moveFailed(AMControl::TimeoutFailure); } }
// Re-implemented from AMReadOnlyPVwStatusControl: void AMPVwStatusControl::onMovingChanged(int isMovingValue) { bool nowMoving = (*statusChecker_)(isMovingValue); // according to the hardware. For checking moveSucceeded/moveStarted/moveFailed, use the value delivered in the signal argument, instead of re-checking the PV, in case we respond late and the hardware has already changed again. // In case the hardware is being silly and sending multiple MOVE ACTIVE, MOVE ACTIVE, MOVE ACTIVE states in a row, or MOVE DONE, MOVE DONE, MOVE DONE states in a row: only act on changes. [Edge detection] if(nowMoving == hardwareWasMoving_) return; hardwareWasMoving_ = nowMoving; // moveStarted, moveFailed, moveSucceeded, or transition to settling: /////////////////////////////////////////////////////////////////////// // if we requested one of our moves, and moving just started: if(startInProgress_ && nowMoving) { moveInProgress_ = true; startInProgress_ = false; // This is great... the device started moving within the timeout: // disable the moveStartTimer, we don't need it anymore moveStartTimer_.stop(); emit moveStarted(); } // If one of our moves was running, and we stopped moving: if(moveInProgress_ && !nowMoving) { // Mode 1: No settling: if( settlingTime_ == 0.0) { // That's the end of our move moveInProgress_ = false; // Check if we succeeded... if(inPosition()) { emit moveSucceeded(); } else { emit moveFailed(AMControl::ToleranceFailure); } } // Mode 2: allow settling else { if(!settlingInProgress_) { settlingInProgress_ = true; settlingTimer_.start(int(settlingTime_*1000)); // QTimer uses millisecond time intervals. } } } // "sucessfully" stopped due to a stop() command. if(stopInProgress_ && !nowMoving) { stopInProgress_ = false; // but the move itself has failed, due to a stop() intervention. emit moveFailed(AMControl::WasStoppedFailure); } // Emitting movingChanged(). ///////////////////////////////////// // For external purposes, isMoving() depends on whether the hardware says we're moving, or we're in the settling phase. nowMoving = isMoving(); if(nowMoving != wasMoving_) emit movingChanged(wasMoving_ = nowMoving); }
// 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; }