예제 #1
0
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);
}
void AMExtendedControlEditor::setControl(AMControl *newControl)
{
	if (control_ != newControl) {

		if (control_) {

			// Disconnect from control.

			disconnect( control_, 0, this, 0 );
			disconnect( dialog_, 0, control_, 0 );

			control_ = 0;

			// Clear the frame title.

			if (!titleSetManually_)
				setTitleText("");
		}

		control_ = newControl;

		if (control_) {

			// Set the frame title.

			if (!titleSetManually_) {
				if (control_->description().isEmpty())
					setTitleText(control_->name());
				else
					setTitleText(control_->description());
			}

			// Set control values
			precision_ = control_->displayPrecision();
			maxValue_ = control_->maximumValue();
			minValue_ = control_->minimumValue();

			// Make connections.
			connect(control_, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double)));
			connect(control_, SIGNAL(unitsChanged(QString)), this, SLOT(onUnitsChanged(QString)));
			connect(control_, SIGNAL(connected(bool)), this, SLOT(onConnectedChanged()) );
			connect(control_, SIGNAL(movingChanged(bool)), this, SLOT(onMotion(bool)));
			connect(control_, SIGNAL(enumChanged()), this, SLOT(onControlEnumChanged()));
			connect(control_, SIGNAL(moveStarted()), this, SLOT(onControlMoveStarted()));

			if(!configureOnly_)
				connect(dialog_, SIGNAL(doubleValueSelected(double)), control_, SLOT(move(double)));
			else
				connect(dialog_, SIGNAL(doubleValueSelected(double)), this, SLOT(onNewSetpoint(double)));
		}

		onConnectedChanged();

		emit controlChanged(control_);
	}
}
예제 #3
0
// 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;
}
예제 #4
0
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);
}
예제 #5
0
// This is used to handle the timeout of a move start:
void CLSMDriveMotorControl::onMoveStartTimeout() {

    moveStartTimer_.stop();

    // This is only meaningful if one of our moves is in progress.
    if(startInProgress_) {
        // give up on this move:
        startInProgress_ = false;

        // Special case: only applies if moveTimeoutTolerance_ != 0 AND we've gotten within moveTimeoutTolerance_ of the setpoint.
        if(moveTimeoutTolerance() != 0.0 && fabs(setpoint() - value()) < moveTimeoutTolerance_) {
            moveInProgress_ = true;
            emit moveStarted();
            moveInProgress_ = false;
            emit moveSucceeded();
        }
        else {
            // The move didn't start within our allowed start period. That counts as a move failed.
            emit moveFailed(AMControl::TimeoutFailure);
        }
    }
}
예제 #6
0
// 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);
}
예제 #7
0
// 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;
}
예제 #8
0
AMControl::FailureExplanation REIXSSpectrometer::move(double setpoint)
{
	if(!isConnected()) {
		AMErrorMon::report(AMErrorReport(this, AMErrorReport::Alert, 2, "Can't move the spectrometer: some motor controls are not connected. Check that the IOCs are running and the network connections are good."));
		return NotConnectedFailure;
	}

	// can't start a move while moving
	if(moveInProgress() || isMoving()) {
		AMErrorMon::report(AMErrorReport(this, AMErrorReport::Alert, 2, "Can't move the spectrometer, because it's already moving.  Stop the spectrometer first before attempting another move."));
		return AlreadyMovingFailure;
	}

	specifiedEV_ = setpoint;
	currentFocusOffset_ = specifiedFocusOffset_;
	currentDetectorTiltOffset_ = specifiedDetectorTiltOffset_;

	if(currentGrating_ != specifiedGrating_) {
		emit gratingChanged(currentGrating_ = specifiedGrating_);
	}

	/// \todo limits? out of range?
	moveSetpoint_ = calibration_.computeSpectrometerPosition(specifiedGrating_, specifiedEV_, currentFocusOffset_, currentDetectorTiltOffset_);

	// build the move action (With sub-actions to run in parallel)
	moveAction_ = new AMListAction3(new AMListActionInfo3("spectrometer eV move", "spectrometer eV move"), AMListAction3::Parallel, this);
	// if we need to move the grating into position, add actions for that:
	if(!gratingInPosition()) {
		// make a sequential action for the grating moves
		AMListAction3* gratingAction = new AMListAction3(new AMListActionInfo3("grating move", "grating move"));	// sequential by default.
		// first, move Z to 0
		gratingAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->z(), 0));
		// then move U,V,W to 0
		AMListAction3* uvw0Action = new AMListAction3(new AMListActionInfo3("grating UVW move to 0"), AMListAction3::Parallel);
		uvw0Action->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->u(), 0));
		uvw0Action->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->v(), 0));
		uvw0Action->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->w(), 0));
		gratingAction->addSubAction(uvw0Action);
		// then move R,S,T into position (Can do simultaneously with parallel action)
		AMListAction3* rstAction = new AMListAction3(new AMListActionInfo3("grating RST move"), AMListAction3::Parallel);
		rstAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->r(), moveSetpoint_.controlNamed("hexapodR").value()));
		rstAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->s(), moveSetpoint_.controlNamed("hexapodS").value()));
		rstAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->t(), moveSetpoint_.controlNamed("hexapodT").value()));
		gratingAction->addSubAction(rstAction);
		// move U,V,W to actual position
		AMListAction3* uvwAction = new AMListAction3(new AMListActionInfo3("grating UVW move"), AMListAction3::Parallel);
		uvwAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->u(), moveSetpoint_.controlNamed("hexapodU").value()));
		uvwAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->v(), moveSetpoint_.controlNamed("hexapodV").value()));
		uvwAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->w(), moveSetpoint_.controlNamed("hexapodW").value()));
		gratingAction->addSubAction(uvwAction);
		// then move X,Y,Z into position (can do simultaneously with parallel action)
		AMListAction3* xyzAction = new AMListAction3(new AMListActionInfo3("grating XYZ move"), AMListAction3::Parallel);
		xyzAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->x(), moveSetpoint_.controlNamed("hexapodX").value()));
		xyzAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->y(), moveSetpoint_.controlNamed("hexapodY").value()));
		xyzAction->addSubAction(AMActionSupport::buildControlMoveAction(hexapod_->z(), moveSetpoint_.controlNamed("hexapodZ").value()));
		gratingAction->addSubAction(xyzAction);

		moveAction_->addSubAction(gratingAction);
	}
	// add Lift, Tilt, and Translation
	moveAction_->addSubAction(AMActionSupport::buildControlMoveAction(spectrometerRotationDrive_, moveSetpoint_.controlNamed("spectrometerRotationDrive").value()));
	moveAction_->addSubAction(AMActionSupport::buildControlMoveAction(detectorTiltDrive_, moveSetpoint_.controlNamed("detectorTiltDrive").value()));
	moveAction_->addSubAction(AMActionSupport::buildControlMoveAction(detectorTranslation_, moveSetpoint_.controlNamed("detectorTranslation").value()));
	// Disabled for now: moveAction_->addSubAction(new AMInternalControlMoveAction(endstationTranslation_, moveSetpoint_.controlNamed("endstationTranslation").value()))

	// Watch the move action: succeeded or failed (or cancelled)
	connect(moveAction_, SIGNAL(stateChanged(int,int)), this, SLOT(onMoveActionStateChanged(int,int)));
	emit moveStarted();
	moveAction_->start();
	return NoFailure;
}
void AMPIC887EpicsCoordinator::onAllConnected(bool connectedState)
{
	if(connectedState) {

		if(!controller_->isInValidState()) {
			qDebug() << "Initialization: Setting Axes status to Error";
			xAxisStatus_->move(4);
			yAxisStatus_->move(4);
			zAxisStatus_->move(4);
			uAxisStatus_->move(4);
			vAxisStatus_->move(4);
			wAxisStatus_->move(4);
		} else if (!connectedOnce_) {

			connectedOnce_ = true;
			// Initializing x Axis
			if(!xAxisFeedback_->withinTolerance(controller_->currentPosition(AMGCS2::XAxis))) {
				qDebug() << "Initialization: Setting x Axis position to " << controller_->currentPosition(AMGCS2::XAxis);
				xAxisFeedback_->move(controller_->currentPosition(AMGCS2::XAxis));
			}

			if(controller_->movementStatuses().testFlag(AMGCS2::XAxisIsMoving) && !xAxisStatus_->withinTolerance(1)) {
				qDebug() << "Initialization: Setting x Axis status to moving";
				xAxisStatus_->move(1);
			} else if (!xAxisStatus_->withinTolerance(0)) {
				qDebug() << "Initialization: Setting x Axis status to move done";
				xAxisStatus_->move(0);
			}

			// Initializing y Axis
			if(!yAxisFeedback_->withinTolerance(controller_->currentPosition(AMGCS2::YAxis))) {
				qDebug() << "Initialzation: Setting y Axis position to " << controller_->currentPosition(AMGCS2::YAxis);
				yAxisFeedback_->move(controller_->currentPosition(AMGCS2::YAxis));
			}

			if(controller_->movementStatuses().testFlag(AMGCS2::YAxisIsMoving) && !yAxisStatus_->withinTolerance(1)) {
				qDebug() << "Initialization: Setting y Axis status to moving";
				yAxisStatus_->move(1);
			} else if (!yAxisStatus_->withinTolerance(0)) {
				qDebug() << "Initialization: Setting y Axis status to move done";
				yAxisStatus_->move(0);
			}

			// Initializing z Axis
			if(!zAxisFeedback_->withinTolerance(controller_->currentPosition(AMGCS2::ZAxis))) {
				qDebug() << "Initialzation: Setting z Axis position to " << controller_->currentPosition(AMGCS2::ZAxis);
				zAxisFeedback_->move(controller_->currentPosition(AMGCS2::ZAxis));
			}

			if(controller_->movementStatuses().testFlag(AMGCS2::ZAxisIsMoving) && !zAxisStatus_->withinTolerance(1)) {
				qDebug() << "Initialization: Setting z Axis status to moving";
				zAxisStatus_->move(1);
			} else if (!zAxisStatus_->withinTolerance(0)) {
				qDebug() << "Initialization: Setting z Axis status to move done";
				zAxisStatus_->move(0);
			}

			// Initializing u Axis
			if(!uAxisFeedback_->withinTolerance(controller_->currentPosition(AMGCS2::UAxis))) {
				qDebug() << "Initialzation: Setting u Axis position to " << controller_->currentPosition(AMGCS2::UAxis);
				uAxisFeedback_->move(controller_->currentPosition(AMGCS2::UAxis));
			}

			if(controller_->movementStatuses().testFlag(AMGCS2::UAxisIsMoving) && !uAxisStatus_->withinTolerance(1)) {
				qDebug() << "Initialization: Setting u Axis status to moving";
				uAxisStatus_->move(1);
			} else if (!uAxisStatus_->withinTolerance(0)) {
				qDebug() << "Initialization: Setting u Axis status to move done";
				uAxisStatus_->move(0);
			}

			// Initializing v Axis
			if(!vAxisFeedback_->withinTolerance(controller_->currentPosition(AMGCS2::VAxis))) {
				qDebug() << "Initialzation: Setting v Axis position to " << controller_->currentPosition(AMGCS2::VAxis);
				vAxisFeedback_->move(controller_->currentPosition(AMGCS2::VAxis));
			}

			if(controller_->movementStatuses().testFlag(AMGCS2::VAxisIsMoving) && !vAxisStatus_->withinTolerance(1)) {
				qDebug() << "Initialization: Setting v Axis status to moving";
				vAxisStatus_->move(1);
			} else if (!vAxisStatus_->withinTolerance(0)) {
				qDebug() << "Initialization: Setting v Axis status to move done";
				vAxisStatus_->move(0);
			}

			// Initializing w Axis
			if(!wAxisFeedback_->withinTolerance(controller_->currentPosition(AMGCS2::WAxis))) {
				qDebug() << "Initialzation: Setting w Axis position to " << controller_->currentPosition(AMGCS2::WAxis);
				wAxisFeedback_->move(controller_->currentPosition(AMGCS2::WAxis));
			}

			if(controller_->movementStatuses().testFlag(AMGCS2::WAxisIsMoving) && !wAxisStatus_->withinTolerance(1)) {
				qDebug() << "Initialization: Setting w Axis status to moving";
				wAxisStatus_->move(1);
			} else if (!wAxisStatus_->withinTolerance(0)) {
				qDebug() << "Initialization: Setting w Axis status to move done";
				wAxisStatus_->move(0);
			}

			// Initialize system velocity
			if(!systemVelocityFeedback_->withinTolerance(controller_->systemVelocity())) {
				qDebug() << "Initialization: Setting system velocity to " << controller_->systemVelocity();
				systemVelocityFeedback_->move(controller_->systemVelocity());
			}

			if(!systemVelocitySetpoint_->withinTolerance(controller_->systemVelocity())) {
				qDebug() << "Initialization: Setting system velocity setpoint to " << controller_->systemVelocity();
				systemVelocitySetpoint_->move(controller_->systemVelocity());
			}

			// Initialize the record rate.
			if(!recordRateFeedback_->withinTolerance(controller_->recordRate())) {
				qDebug() << "Initialization: Setting record rate to " << controller_->recordRate();
				recordRateFeedback_->move(controller_->recordRate());
			}

			if(!recordRateSetpoint_->withinTolerance(controller_->recordRate())) {
				qDebug() << "Initialization: Setting record rate setpoint to " << controller_->recordRate();
				recordRateSetpoint_->move(controller_->recordRate());
			}

			// Initialize data recorder active state
			double dataRecorderActiveValue = controller_->isDataRecorderActive() ? 1.0 : 0.0;
			if(dataRecorderActive_->withinTolerance(dataRecorderActiveValue)) {
				qDebug() << "Initialization: Setting data recorder active status to " << dataRecorderActiveValue;
				dataRecorderActive_->move(dataRecorderActiveValue);
			}
		}

		connect(systemVelocitySetpoint_, SIGNAL(valueChanged(double)), this, SLOT(onSystemVelocitySetpointChanged(double)));
		connect(recordRateSetpoint_, SIGNAL(valueChanged(double)), this, SLOT(onRecordRateSetpointPVChanged(double)));
		connect(dataRecorderActive_, SIGNAL(valueChanged(double)), this, SLOT(onDataRecorderActivePVChanged(double)));
		connect(stopAll_, SIGNAL(valueChanged(double)), this, SLOT(onStopAll(double)));

		connect(controller_, SIGNAL(moveStarted(AMGCS2::AxisMovementStatuses)), this, SLOT(onMotionStartedChanged(AMGCS2::AxisMovementStatuses)));
		connect(controller_, SIGNAL(moveFailed(AMGCS2::AxisMovementStatuses)), this, SLOT(onMotionFailed(AMGCS2::AxisMovementStatuses)));
		connect(controller_, SIGNAL(moveComplete()), this, SLOT(onMotionCompleted()));
		connect(controller_, SIGNAL(positionUpdate(AMPIC887AxisMap<double>)), this, SLOT(onPositionUpdate(AMPIC887AxisMap<double>)));
		connect(controller_, SIGNAL(systemVelocityChanged(double)), this, SLOT(onSystemVelocityChanged(double)));
		connect(controller_, SIGNAL(recordRateChanged(double)), this, SLOT(onControllerRecordRateChanged(double)));
		connect(controller_, SIGNAL(dataRecorderActiveStateChanged(bool)), this, SLOT(onControllerDataRecorderActiveChanged(bool)));
	}
}