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,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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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));

View File

@ -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) {