Fix repeated re-homing + add more unit tests for that scenario
parent
f25f88b164
commit
bcba966a0e
|
|
@ -82,34 +82,41 @@ bool CommandBase::WaitForOneModuleErrorRecovery(ErrorCode ec, modules::motion::M
|
|||
stateBeforeModuleFailed = state;
|
||||
error = ec;
|
||||
state = ProgressCode::ERRWaitingForUser; // such a situation always requires user's attention -> let the printer display an error screen
|
||||
}
|
||||
|
||||
// are we already recovering an error - that would mean we got another one
|
||||
if (recoveringMovableError) {
|
||||
error = ec;
|
||||
state = ProgressCode::ERRWaitingForUser; // such a situation always requires user's attention -> let the printer display an error screen
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case ProgressCode::ERRWaitingForUser: // waiting for a recovery - mask axis bits:
|
||||
if (WithoutAxisBits(ec) == ErrorCode::HOMING_FAILED) {
|
||||
// homing can be recovered
|
||||
mui::Event ev = mui::userInput.ConsumeEvent();
|
||||
if (ev == mui::Event::Middle) {
|
||||
recoveringMovableError = true;
|
||||
m.PlanHome(); // force initiate a new homing attempt
|
||||
state = ProgressCode::Homing;
|
||||
error = ErrorCode::RUNNING;
|
||||
}
|
||||
}
|
||||
// TMC errors cannot be recovered safely, waiting for power cycling the MMU
|
||||
return true;
|
||||
} else {
|
||||
switch (state) {
|
||||
case ProgressCode::ERRWaitingForUser: // waiting for a recovery - mask axis bits:
|
||||
if (WithoutAxisBits(ec) == ErrorCode::HOMING_FAILED) {
|
||||
// homing can be recovered
|
||||
mui::Event ev = mui::userInput.ConsumeEvent();
|
||||
if (ev == mui::Event::Middle) {
|
||||
m.PlanHome(); // force initiate a new homing attempt
|
||||
state = ProgressCode::Homing;
|
||||
error = ErrorCode::RUNNING;
|
||||
}
|
||||
}
|
||||
// TMC errors cannot be recovered safely, waiting for power cycling the MMU
|
||||
return true;
|
||||
case ProgressCode::Homing:
|
||||
if (m.HomingValid()) {
|
||||
// managed to recover from a homing problem
|
||||
state = stateBeforeModuleFailed;
|
||||
stateBeforeModuleFailed = ProgressCode::OK;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return true; // no idea what to do in other states ... set internal fw error state?
|
||||
case ProgressCode::Homing:
|
||||
if (m.HomingValid()) {
|
||||
// managed to recover from a homing problem
|
||||
state = stateBeforeModuleFailed;
|
||||
recoveringMovableError = false;
|
||||
stateBeforeModuleFailed = ProgressCode::OK;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return true; // no idea what to do in other states ... set internal fw error state?
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ public:
|
|||
inline CommandBase()
|
||||
: state(ProgressCode::OK)
|
||||
, error(ErrorCode::OK)
|
||||
, stateBeforeModuleFailed(ProgressCode::OK) {}
|
||||
, stateBeforeModuleFailed(ProgressCode::OK)
|
||||
, recoveringMovableError(false) {}
|
||||
|
||||
// Normally, a base class should (must) have a virtual destructor to enable correct deallocation of superstructures.
|
||||
// However, in our case we don't want ANY destruction of these objects and moreover - adding a destructor like this
|
||||
|
|
@ -110,6 +111,7 @@ protected:
|
|||
ProgressCode state; ///< current progress state of the state machine
|
||||
ErrorCode error; ///< current error code
|
||||
ProgressCode stateBeforeModuleFailed; ///< saved state of the state machine before a common error happened
|
||||
bool recoveringMovableError;
|
||||
};
|
||||
|
||||
} // namespace logic
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void MovableBase::PerformHomeBack() {
|
|||
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
|
||||
if (!FinishHomingAndPlanMoveToParkPos()) {
|
||||
// the measured axis' length was incorrect, something is blocking it, report an error, homing procedure terminated
|
||||
state = HomingFailed;
|
||||
HomeFailed();
|
||||
} else {
|
||||
homingValid = true;
|
||||
// state = Ready; // not yet - we have to move to our parking position after homing the axis
|
||||
|
|
@ -74,9 +74,10 @@ void MovableBase::PerformHomeBack() {
|
|||
}
|
||||
|
||||
void MovableBase::HomeFailed() {
|
||||
// we ran out of planned moves but no StallGuard event has occurred - homing failed
|
||||
// we ran out of planned moves but no StallGuard event has occurred
|
||||
// or the measured length of axis was not within the accepted tolerance
|
||||
homingValid = false;
|
||||
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
|
||||
mm::motion.Disable(axis); // disable power to the axis - allows the user to do something with the device manually
|
||||
state = HomingFailed;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,21 +58,35 @@ TEST_CASE("homing::successful_run", "[homing]") {
|
|||
}
|
||||
}
|
||||
|
||||
bool SelectorFailedRetry() {
|
||||
// prepare startup conditions
|
||||
ForceReinitAllAutomata();
|
||||
template <typename T>
|
||||
bool SimulateFailedHomePostfix(T &h) {
|
||||
REQUIRE(WhileTopState(h, ProgressCode::Homing, 5));
|
||||
REQUIRE(mi::idler.HomingValid());
|
||||
|
||||
// change the startup to what we need here
|
||||
EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley);
|
||||
REQUIRE(h.Error() == ErrorCode::HOMING_SELECTOR_FAILED);
|
||||
REQUIRE(h.State() == ProgressCode::ERRWaitingForUser);
|
||||
REQUIRE_FALSE(mm::motion.Enabled(mm::Selector));
|
||||
|
||||
// set FINDA OFF + debounce
|
||||
SetFINDAStateAndDebounce(false);
|
||||
// do a few steps before pushing the button
|
||||
WhileTopState(h, ProgressCode::ERRWaitingForUser, 5);
|
||||
|
||||
logic::Home h;
|
||||
REQUIRE(VerifyState(h, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), 0, false, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK));
|
||||
REQUIRE_FALSE(mm::motion.Enabled(mm::Selector));
|
||||
|
||||
h.Reset(0);
|
||||
REQUIRE(VerifyState(h, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), 0, false, false, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::Homing));
|
||||
PressButtonAndDebounce(h, mb::Middle);
|
||||
|
||||
// it shall start homing again
|
||||
REQUIRE(h.Error() == ErrorCode::RUNNING);
|
||||
REQUIRE(h.State() == ProgressCode::Homing);
|
||||
REQUIRE_FALSE(ms::selector.HomingValid());
|
||||
REQUIRE(mm::motion.Enabled(mm::Selector));
|
||||
|
||||
ClearButtons(h);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SimulateFailedHomeFirstTime(T &h) {
|
||||
REQUIRE_FALSE(mi::idler.HomingValid());
|
||||
REQUIRE_FALSE(ms::selector.HomingValid());
|
||||
|
||||
|
|
@ -115,28 +129,71 @@ bool SelectorFailedRetry() {
|
|||
}
|
||||
}
|
||||
|
||||
// now the Selector shall perform a move into their parking positions
|
||||
while (ms::selector.State() != mm::MovableBase::HomingFailed)
|
||||
main_loop();
|
||||
}
|
||||
|
||||
REQUIRE(WhileTopState(h, ProgressCode::Homing, 5));
|
||||
REQUIRE(mi::idler.HomingValid());
|
||||
return SimulateFailedHomePostfix(h);
|
||||
}
|
||||
|
||||
// cannot check the whole environment easily, the selector's and idler's positions are elsewhere
|
||||
REQUIRE(h.Error() == ErrorCode::HOMING_SELECTOR_FAILED);
|
||||
REQUIRE(h.State() == ProgressCode::ERRWaitingForUser);
|
||||
|
||||
// do a few steps before pushing the button
|
||||
WhileTopState(h, ProgressCode::ERRWaitingForUser, 5);
|
||||
|
||||
PressButtonAndDebounce(h, mb::Middle);
|
||||
|
||||
// it shall start homing again
|
||||
REQUIRE(h.Error() == ErrorCode::RUNNING);
|
||||
REQUIRE(h.State() == ProgressCode::Homing);
|
||||
template <typename T>
|
||||
bool SimulateFailedHomeSelectorRepeated(T &h) {
|
||||
// we leave Idler aside in this case
|
||||
REQUIRE_FALSE(ms::selector.HomingValid());
|
||||
|
||||
{
|
||||
// do 5 steps until we trigger the simulated stallguard
|
||||
for (uint8_t i = 0; i < 5; ++i) {
|
||||
main_loop();
|
||||
}
|
||||
|
||||
mm::TriggerStallGuard(mm::Selector);
|
||||
main_loop();
|
||||
mm::motion.StallGuardReset(mm::Selector);
|
||||
}
|
||||
uint32_t selectorSteps = mm::unitToSteps<mm::S_pos_t>(config::selectorLimits.lenght) + 1;
|
||||
uint32_t selectorTriggerShort = selectorSteps / 2;
|
||||
uint32_t maxSteps = selectorTriggerShort + 1;
|
||||
{
|
||||
for (uint32_t i = 0; i < maxSteps; ++i) {
|
||||
main_loop();
|
||||
|
||||
if (i == selectorTriggerShort) {
|
||||
mm::TriggerStallGuard(mm::Selector);
|
||||
} else {
|
||||
mm::motion.StallGuardReset(mm::Selector);
|
||||
}
|
||||
}
|
||||
|
||||
while (ms::selector.State() != mm::MovableBase::HomingFailed)
|
||||
main_loop();
|
||||
}
|
||||
|
||||
return SimulateFailedHomePostfix(h);
|
||||
}
|
||||
|
||||
bool SelectorFailedRetry() {
|
||||
// prepare startup conditions
|
||||
ForceReinitAllAutomata();
|
||||
|
||||
// change the startup to what we need here
|
||||
EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley);
|
||||
|
||||
// set FINDA OFF + debounce
|
||||
SetFINDAStateAndDebounce(false);
|
||||
|
||||
logic::Home h;
|
||||
REQUIRE(VerifyState(h, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), 0, false, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK));
|
||||
|
||||
h.Reset(0);
|
||||
REQUIRE(VerifyState(h, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), 0, false, false, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::Homing));
|
||||
|
||||
REQUIRE(SimulateFailedHomeFirstTime(h));
|
||||
|
||||
for (uint8_t i = 0; i < 5; ++i) {
|
||||
REQUIRE(SimulateFailedHomeSelectorRepeated(h));
|
||||
}
|
||||
|
||||
SimulateSelectorHoming();
|
||||
|
||||
REQUIRE(WhileTopState(h, ProgressCode::Homing, 5000));
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ AxisSim axes[3] = {
|
|||
};
|
||||
|
||||
bool Motion::InitAxis(Axis axis) {
|
||||
axes[axis].enabled = true;
|
||||
SetEnabled(axis, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Motion::SetEnabled(Axis axis, bool enabled) {
|
||||
axes[axis].enabled = enabled;
|
||||
axisData[axis].enabled = axes[axis].enabled = enabled;
|
||||
}
|
||||
|
||||
bool Motion::StallGuard(Axis axis) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue