Fix repeated re-homing + add more unit tests for that scenario

pull/154/head
D.R.racer 2022-02-03 13:57:38 +01:00 committed by DRracer
parent f25f88b164
commit bcba966a0e
5 changed files with 123 additions and 56 deletions

View File

@ -82,14 +82,21 @@ bool CommandBase::WaitForOneModuleErrorRecovery(ErrorCode ec, modules::motion::M
stateBeforeModuleFailed = state; stateBeforeModuleFailed = state;
error = ec; error = ec;
state = ProgressCode::ERRWaitingForUser; // such a situation always requires user's attention -> let the printer display an error screen state = ProgressCode::ERRWaitingForUser; // such a situation always requires user's attention -> let the printer display an error screen
return true; }
} else {
// 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) { switch (state) {
case ProgressCode::ERRWaitingForUser: // waiting for a recovery - mask axis bits: case ProgressCode::ERRWaitingForUser: // waiting for a recovery - mask axis bits:
if (WithoutAxisBits(ec) == ErrorCode::HOMING_FAILED) { if (WithoutAxisBits(ec) == ErrorCode::HOMING_FAILED) {
// homing can be recovered // homing can be recovered
mui::Event ev = mui::userInput.ConsumeEvent(); mui::Event ev = mui::userInput.ConsumeEvent();
if (ev == mui::Event::Middle) { if (ev == mui::Event::Middle) {
recoveringMovableError = true;
m.PlanHome(); // force initiate a new homing attempt m.PlanHome(); // force initiate a new homing attempt
state = ProgressCode::Homing; state = ProgressCode::Homing;
error = ErrorCode::RUNNING; error = ErrorCode::RUNNING;
@ -101,6 +108,7 @@ bool CommandBase::WaitForOneModuleErrorRecovery(ErrorCode ec, modules::motion::M
if (m.HomingValid()) { if (m.HomingValid()) {
// managed to recover from a homing problem // managed to recover from a homing problem
state = stateBeforeModuleFailed; state = stateBeforeModuleFailed;
recoveringMovableError = false;
stateBeforeModuleFailed = ProgressCode::OK; stateBeforeModuleFailed = ProgressCode::OK;
return false; return false;
} }
@ -110,7 +118,6 @@ bool CommandBase::WaitForOneModuleErrorRecovery(ErrorCode ec, modules::motion::M
} }
return true; return true;
} }
}
return false; return false;
} }

View File

@ -26,7 +26,8 @@ public:
inline CommandBase() inline CommandBase()
: state(ProgressCode::OK) : state(ProgressCode::OK)
, error(ErrorCode::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. // 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 // 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 ProgressCode state; ///< current progress state of the state machine
ErrorCode error; ///< current error code ErrorCode error; ///< current error code
ProgressCode stateBeforeModuleFailed; ///< saved state of the state machine before a common error happened ProgressCode stateBeforeModuleFailed; ///< saved state of the state machine before a common error happened
bool recoveringMovableError;
}; };
} // namespace logic } // namespace logic

View File

@ -63,7 +63,7 @@ void MovableBase::PerformHomeBack() {
mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal); mm::motion.SetMode(axis, mg::globals.MotorsStealth() ? mm::Stealth : mm::Normal);
if (!FinishHomingAndPlanMoveToParkPos()) { if (!FinishHomingAndPlanMoveToParkPos()) {
// the measured axis' length was incorrect, something is blocking it, report an error, homing procedure terminated // the measured axis' length was incorrect, something is blocking it, report an error, homing procedure terminated
state = HomingFailed; HomeFailed();
} else { } else {
homingValid = true; homingValid = true;
// state = Ready; // not yet - we have to move to our parking position after homing the axis // 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() { 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; 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; state = HomingFailed;
} }

View File

@ -58,21 +58,35 @@ TEST_CASE("homing::successful_run", "[homing]") {
} }
} }
bool SelectorFailedRetry() { template <typename T>
// prepare startup conditions bool SimulateFailedHomePostfix(T &h) {
ForceReinitAllAutomata(); REQUIRE(WhileTopState(h, ProgressCode::Homing, 5));
REQUIRE(mi::idler.HomingValid());
// change the startup to what we need here REQUIRE(h.Error() == ErrorCode::HOMING_SELECTOR_FAILED);
EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley); REQUIRE(h.State() == ProgressCode::ERRWaitingForUser);
REQUIRE_FALSE(mm::motion.Enabled(mm::Selector));
// set FINDA OFF + debounce // do a few steps before pushing the button
SetFINDAStateAndDebounce(false); WhileTopState(h, ProgressCode::ERRWaitingForUser, 5);
logic::Home h; REQUIRE_FALSE(mm::motion.Enabled(mm::Selector));
REQUIRE(VerifyState(h, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), 0, false, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK));
h.Reset(0); PressButtonAndDebounce(h, mb::Middle);
REQUIRE(VerifyState(h, mg::FilamentLoadState::AtPulley, mi::Idler::IdleSlotIndex(), 0, false, false, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::Homing));
// 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(mi::idler.HomingValid());
REQUIRE_FALSE(ms::selector.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) while (ms::selector.State() != mm::MovableBase::HomingFailed)
main_loop(); main_loop();
} }
REQUIRE(WhileTopState(h, ProgressCode::Homing, 5)); return SimulateFailedHomePostfix(h);
REQUIRE(mi::idler.HomingValid()); }
// cannot check the whole environment easily, the selector's and idler's positions are elsewhere template <typename T>
REQUIRE(h.Error() == ErrorCode::HOMING_SELECTOR_FAILED); bool SimulateFailedHomeSelectorRepeated(T &h) {
REQUIRE(h.State() == ProgressCode::ERRWaitingForUser); // we leave Idler aside in this case
// 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);
REQUIRE_FALSE(ms::selector.HomingValid()); 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(); SimulateSelectorHoming();
REQUIRE(WhileTopState(h, ProgressCode::Homing, 5000)); REQUIRE(WhileTopState(h, ProgressCode::Homing, 5000));

View File

@ -16,12 +16,12 @@ AxisSim axes[3] = {
}; };
bool Motion::InitAxis(Axis axis) { bool Motion::InitAxis(Axis axis) {
axes[axis].enabled = true; SetEnabled(axis, true);
return true; return true;
} }
void Motion::SetEnabled(Axis axis, bool enabled) { void Motion::SetEnabled(Axis axis, bool enabled) {
axes[axis].enabled = enabled; axisData[axis].enabled = axes[axis].enabled = enabled;
} }
bool Motion::StallGuard(Axis axis) { bool Motion::StallGuard(Axis axis) {