Unit tests: selector refused to move in ToolChange

Revealed all kinds of subtle issues (which is great). All have been fixed in this commit.
pull/239/head
D.R.racer 2022-11-16 10:19:49 +01:00
parent ba1bbbcf0a
commit 33b95f5d00
8 changed files with 125 additions and 16 deletions

View File

@ -18,9 +18,15 @@ bool FeedToFinda::Reset(bool feedPhaseLimited, bool haltAtEnd) {
this->feedPhaseLimited = feedPhaseLimited; this->feedPhaseLimited = feedPhaseLimited;
this->haltAtEnd = haltAtEnd; this->haltAtEnd = haltAtEnd;
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off); ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
mi::idler.Engage(mg::globals.ActiveSlot()); if (ms::selector.MoveToSlot(mg::globals.ActiveSlot()) != ms::Selector::OperationResult::Accepted) {
// We can't get any FINDA readings if the selector is at the wrong spot - move it accordingly if necessary // We can't get any FINDA readings if the selector is at the wrong spot - move it accordingly if necessary
return ms::selector.MoveToSlot(mg::globals.ActiveSlot()) == ms::Selector::OperationResult::Accepted; // And prevent issuing any commands to the idler in such an error state
return false;
} else {
// Selector accepted the command, we can plan the Idler as well
mi::idler.Engage(mg::globals.ActiveSlot());
return true;
}
} }
bool FeedToFinda::Step() { bool FeedToFinda::Step() {

View File

@ -47,9 +47,13 @@ void LoadFilament::ResetLimited(uint8_t param) {
void logic::LoadFilament::Reset2(bool feedPhaseLimited) { void logic::LoadFilament::Reset2(bool feedPhaseLimited) {
state = ProgressCode::FeedingToFinda; state = ProgressCode::FeedingToFinda;
error = ErrorCode::RUNNING; error = ErrorCode::RUNNING;
feed.Reset(feedPhaseLimited, true); if (!feed.Reset(feedPhaseLimited, true)) {
// selector refused to move
GoToErrDisengagingIdler(ErrorCode::FINDA_DIDNT_SWITCH_OFF);
} else {
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off); ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
} }
}
void logic::LoadFilament::GoToRetractingFromFinda() { void logic::LoadFilament::GoToRetractingFromFinda() {
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off); ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);

View File

@ -39,8 +39,10 @@ bool ToolChange::Reset(uint8_t param) {
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) { if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
dbg_logic_P(PSTR("Filament is loaded --> unload")); dbg_logic_P(PSTR("Filament is loaded --> unload"));
state = ProgressCode::UnloadingFilament; state = ProgressCode::UnloadingFilament;
unloadAlreadyFinished = false;
unl.Reset(mg::globals.ActiveSlot()); unl.Reset(mg::globals.ActiveSlot());
} else { } else {
unloadAlreadyFinished = true;
if (feed.Reset(true, false)) { if (feed.Reset(true, false)) {
state = ProgressCode::FeedingToFinda; state = ProgressCode::FeedingToFinda;
error = ErrorCode::RUNNING; error = ErrorCode::RUNNING;
@ -71,7 +73,9 @@ void logic::ToolChange::GoToFeedingToFinda() {
state = ProgressCode::FeedingToFinda; state = ProgressCode::FeedingToFinda;
error = ErrorCode::RUNNING; error = ErrorCode::RUNNING;
mg::globals.SetFilamentLoaded(plannedSlot, mg::FilamentLoadState::AtPulley); mg::globals.SetFilamentLoaded(plannedSlot, mg::FilamentLoadState::AtPulley);
feed.Reset(true, false); if (!feed.Reset(true, false)) {
GoToErrDisengagingIdler(ErrorCode::FINDA_DIDNT_SWITCH_OFF);
}
} }
bool ToolChange::StepInner() { bool ToolChange::StepInner() {
@ -81,6 +85,9 @@ bool ToolChange::StepInner() {
// unloading sequence finished - basically, no errors can occurr here // unloading sequence finished - basically, no errors can occurr here
// as UnloadFilament should handle all the possible error states on its own // as UnloadFilament should handle all the possible error states on its own
// There is no way the UnloadFilament to finish in an error state // There is no way the UnloadFilament to finish in an error state
unloadAlreadyFinished = true;
// But planning the next move can fail if Selector refuses moving to the next slot
// - that scenario is handled inside GoToFeedingToFinda
GoToFeedingToFinda(); GoToFeedingToFinda();
} }
break; break;
@ -138,7 +145,13 @@ bool ToolChange::StepInner() {
// Beware: we may run into issues when FINDA or FSensor do not work correctly. Selector may rely on the presumed filament position and actually cut it accidentally when trying to rehome. // Beware: we may run into issues when FINDA or FSensor do not work correctly. Selector may rely on the presumed filament position and actually cut it accidentally when trying to rehome.
// It is yet to be seen if something like this can actually happen. // It is yet to be seen if something like this can actually happen.
InvalidateHoming(); InvalidateHoming();
if (mf::finda.Pressed()) { // This is a tricky part in case FINDA is flickering
// If we already managed to finish the unload, we must assume finda should be NOT pressed.
// If it is still pressed
// -> FINDA is flickering/badly tuned
// -> unreliable and the user didn't fix the issue
// -> we cannot do anything else but request the user to fix FINDA
if (mf::finda.Pressed() && (!unloadAlreadyFinished)) {
Reset(mg::globals.ActiveSlot()); Reset(mg::globals.ActiveSlot());
} else { } else {
GoToFeedingToFinda(); GoToFeedingToFinda();

View File

@ -38,6 +38,10 @@ private:
FeedToFinda feed; FeedToFinda feed;
FeedToBondtech james; // bond ;) FeedToBondtech james; // bond ;)
uint8_t plannedSlot; uint8_t plannedSlot;
/// true if the unload phase was either not necessary or finished correctly.
/// Part of the flickering FINDA issue
bool unloadAlreadyFinished;
}; };
/// The one and only instance of ToolChange state machine in the FW /// The one and only instance of ToolChange state machine in the FW

View File

@ -72,7 +72,7 @@ Idler::OperationResult Idler::Disengage() {
// coordinates invalid, first home, then disengage // coordinates invalid, first home, then disengage
if (!homingValid) { if (!homingValid) {
PerformHomeForward(); PlanHome();
return OperationResult::Accepted; return OperationResult::Accepted;
} }

View File

@ -67,6 +67,12 @@ Selector::OperationResult Selector::MoveToSlot(uint8_t slot) {
return OperationResult::Accepted; return OperationResult::Accepted;
} }
// already at the right slot - prevent invalidating moves when already at the correct spot but FINDA is pressed
if (currentSlot == slot && homingValid) {
dbg_logic_P(PSTR("Moving Selector"));
return OperationResult::Accepted;
}
if (mf::finda.Pressed()) { if (mf::finda.Pressed()) {
// @@TODO not sure why (if) this happens, but anyway - we must not move the selector if FINDA is pressed // @@TODO not sure why (if) this happens, but anyway - we must not move the selector if FINDA is pressed
// That includes the CutFilament operation as well // That includes the CutFilament operation as well
@ -79,12 +85,6 @@ Selector::OperationResult Selector::MoveToSlot(uint8_t slot) {
return OperationResult::Accepted; return OperationResult::Accepted;
} }
// already at the right slot
if (currentSlot == slot) {
dbg_logic_P(PSTR("Moving Selector"));
return OperationResult::Accepted;
}
// do the move // do the move
return InitMovementNoReinitAxis(); return InitMovementNoReinitAxis();
} }

View File

@ -32,7 +32,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
main_loop(); main_loop();
// restart the automaton // restart the automaton
ff.Reset(false, true); REQUIRE(ff.Reset(false, true));
REQUIRE(ff.State() == FeedToFinda::EngagingIdler); REQUIRE(ff.State() == FeedToFinda::EngagingIdler);
@ -100,7 +100,7 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
main_loop(); main_loop();
// restart the automaton - we want the limited version of the feed // restart the automaton - we want the limited version of the feed
ff.Reset(true, true); REQUIRE(ff.Reset(true, true));
REQUIRE(ff.State() == FeedToFinda::EngagingIdler); REQUIRE(ff.State() == FeedToFinda::EngagingIdler);

View File

@ -15,6 +15,7 @@
#include "../../../../src/logic/tool_change.h" #include "../../../../src/logic/tool_change.h"
#include "../../modules/stubs/stub_adc.h" #include "../../modules/stubs/stub_adc.h"
#include "../../modules/stubs/stub_timebase.h"
#include "../stubs/homing.h" #include "../stubs/homing.h"
#include "../stubs/main_loop_stub.h" #include "../stubs/main_loop_stub.h"
@ -407,3 +408,84 @@ TEST_CASE("tool_change::load_fail_FSensor_resolve_btnM", "[tool_change]") {
} }
} }
} }
void ToolChangeWithFlickeringFINDA(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot, bool keepFindaPressed) {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(fromSlot, mg::FilamentLoadState::InNozzle));
SetFINDAStateAndDebounce(true);
SetFSensorStateAndDebounce(true);
// restart the automaton
tc.Reset(toSlot);
REQUIRE(WhileCondition(tc, std::bind(SimulateUnloadToFINDA, _1, 100, 2'000), 200'000));
// This is something else than WhileTopState()==UnloadingFilament
// We need to catch the very moment, when the unload finished and a move to another slot is being planned
REQUIRE(WhileCondition(
tc, [&](uint32_t) -> bool { return tc.unl.State() != ProgressCode::OK; }, 5000));
// now press FINDA again, but prevent stepping other state machines
REQUIRE_FALSE(mf::finda.Pressed());
hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::high);
while (!mf::finda.Pressed()) {
mf::finda.Step();
mt::IncMillis();
}
REQUIRE(mf::finda.Pressed());
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::AtPulley);
// now what ;) - Selector cannot move, because FINDA is pressed
// ToolChange should emit "FINDA_DIDNT_SWITCH_OFF" and we should be able to resolve the error by Retrying
main_loop();
tc.Step();
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, toSlot, toSlot, false, true, ml::off, ml::blink0, ErrorCode::RUNNING, ProgressCode::ERRDisengagingIdler));
SimulateErrDisengagingIdler(tc, ErrorCode::FINDA_DIDNT_SWITCH_OFF); // this should be a single step, Idler should remain disengaged due to previous error
// now we have 2 options what can happen:
// FINDA is still pressed - the user didn't manage to fix the issue
// FINDA is not pressed - the print should continue
if (keepFindaPressed) {
// now waiting for user input
REQUIRE_FALSE(mui::userInput.AnyEvent());
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, toSlot, toSlot, false, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERRWaitingForUser));
PressButtonAndDebounce(tc, mb::Middle, true);
// we should remain in the same error state
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, toSlot, toSlot, false, true, ml::off, ml::blink0, ErrorCode::RUNNING, ProgressCode::ERRDisengagingIdler));
// Idler will try to rehome, allow it
SimulateIdlerHoming(tc);
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, toSlot, toSlot, false, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERRWaitingForUser));
// now "fix" FINDA and the command shall finish correctly
SetFINDAStateAndDebounce(false);
ToolChangeFailLoadToFindaMiddleBtn(tc, toSlot);
} else {
SetFINDAStateAndDebounce(false);
ToolChangeFailLoadToFindaMiddleBtn(tc, toSlot);
}
}
TEST_CASE("tool_change::test_flickering_FINDA", "[tool_change]") {
for (uint8_t fromSlot = 0; fromSlot < config::toolCount; ++fromSlot) {
for (uint8_t toSlot = 0; toSlot < config::toolCount; ++toSlot) {
logic::ToolChange tc;
if (fromSlot != toSlot) {
ToolChangeWithFlickeringFINDA(tc, fromSlot, toSlot, false);
}
}
}
}
TEST_CASE("tool_change::test_flickering_FINDA_keepPressed", "[tool_change]") {
for (uint8_t fromSlot = 0; fromSlot < config::toolCount; ++fromSlot) {
for (uint8_t toSlot = 0; toSlot < config::toolCount; ++toSlot) {
logic::ToolChange tc;
if (fromSlot != toSlot) {
ToolChangeWithFlickeringFINDA(tc, fromSlot, toSlot, true);
}
}
}
}