diff --git a/src/logic/command_base.cpp b/src/logic/command_base.cpp index b2512fb..f8d1784 100644 --- a/src/logic/command_base.cpp +++ b/src/logic/command_base.cpp @@ -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; } diff --git a/src/logic/command_base.h b/src/logic/command_base.h index ed8cd3d..727746c 100644 --- a/src/logic/command_base.h +++ b/src/logic/command_base.h @@ -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 diff --git a/src/modules/movable_base.cpp b/src/modules/movable_base.cpp index 6addf2d..2b3c4f1 100644 --- a/src/modules/movable_base.cpp +++ b/src/modules/movable_base.cpp @@ -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; } diff --git a/tests/unit/logic/homing/test_homing.cpp b/tests/unit/logic/homing/test_homing.cpp index 8ed3708..b8f27ea 100644 --- a/tests/unit/logic/homing/test_homing.cpp +++ b/tests/unit/logic/homing/test_homing.cpp @@ -58,21 +58,35 @@ TEST_CASE("homing::successful_run", "[homing]") { } } -bool SelectorFailedRetry() { - // prepare startup conditions - ForceReinitAllAutomata(); +template +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 +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 +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(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)); diff --git a/tests/unit/logic/stubs/stub_motion.cpp b/tests/unit/logic/stubs/stub_motion.cpp index 3038b27..79061cf 100644 --- a/tests/unit/logic/stubs/stub_motion.cpp +++ b/tests/unit/logic/stubs/stub_motion.cpp @@ -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) {