diff --git a/src/logic/tool_change.h b/src/logic/tool_change.h index 0703e45..2c74b41 100644 --- a/src/logic/tool_change.h +++ b/src/logic/tool_change.h @@ -25,7 +25,9 @@ public: ErrorCode Error() const override; +#ifndef UNITTEST private: +#endif void GoToFeedingToBondtech(); /// Common code for a correct completion of UnloadFilament diff --git a/src/logic/unload_filament.cpp b/src/logic/unload_filament.cpp index d9f8921..6227bc6 100644 --- a/src/logic/unload_filament.cpp +++ b/src/logic/unload_filament.cpp @@ -79,7 +79,7 @@ bool UnloadFilament::StepInner() { } return false; case ProgressCode::DisengagingIdler: - if (!mi::idler.Engaged()) { + if (!mi::idler.Engaged() && ms::selector.State() == ms::Selector::Ready) { FinishedCorrectly(); } return false; diff --git a/tests/unit/logic/helpers/helpers.ipp b/tests/unit/logic/helpers/helpers.ipp index 2521f26..2fea56c 100644 --- a/tests/unit/logic/helpers/helpers.ipp +++ b/tests/unit/logic/helpers/helpers.ipp @@ -87,17 +87,21 @@ bool VerifyState2(SM &uf, mg::FilamentLoadState fls, uint8_t idlerSlotIndex, uin CHECKED_ELSE(mg::globals.FilamentLoaded() & fls) { return false; } - CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex).v) { - return false; + if( idlerSlotIndex < config::toolCount ){ // abusing invalid index to skip checking of slot and position + CHECKED_ELSE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(idlerSlotIndex).v) { + return false; + } + CHECKED_ELSE(mi::idler.Engaged() == (idlerSlotIndex < config::toolCount)) { + return false; + } } - CHECKED_ELSE(mi::idler.Engaged() == (idlerSlotIndex < config::toolCount)) { - return false; - } - CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex).v) { - return false; - } - CHECKED_ELSE(ms::selector.Slot() == selectorSlotIndex) { - return false; + if( selectorSlotIndex < config::toolCount ){ // abusing invalid index to skip checking of slot and position + CHECKED_ELSE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(selectorSlotIndex).v) { + return false; + } + CHECKED_ELSE(ms::selector.Slot() == selectorSlotIndex) { + return false; + } } CHECKED_ELSE(mf::finda.Pressed() == findaPressed) { return false; @@ -116,10 +120,12 @@ bool VerifyState2(SM &uf, mg::FilamentLoadState fls, uint8_t idlerSlotIndex, uin return false; } } else { - CHECKED_ELSE(ml::leds.Mode(ledCheckIndex, ml::red) == redLEDMode) { + auto lmr = ml::leds.Mode(ledCheckIndex, ml::red); + CHECKED_ELSE(lmr == redLEDMode) { return false; } - CHECKED_ELSE(ml::leds.Mode(ledCheckIndex, ml::green) == greenLEDMode) { + auto lmg = ml::leds.Mode(ledCheckIndex, ml::green); + CHECKED_ELSE(lmg == greenLEDMode) { return false; } } diff --git a/tests/unit/logic/stubs/main_loop_stub.cpp b/tests/unit/logic/stubs/main_loop_stub.cpp index 2bd2c92..cc27513 100644 --- a/tests/unit/logic/stubs/main_loop_stub.cpp +++ b/tests/unit/logic/stubs/main_loop_stub.cpp @@ -117,6 +117,20 @@ bool SimulateUnloadToFINDA(uint32_t step, uint32_t fsOff, uint32_t findaOff) { return mf::finda.Pressed(); } +bool SimulateFeedToFINDA(uint32_t step, uint32_t findaOn) { + if (step == findaOn) { + hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::high); + } + return !mf::finda.Pressed(); +} + +bool SimulateRetractFromFINDA(uint32_t step, uint32_t findaOff) { + if (step == findaOff) { + hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::low); + } + return mf::finda.Pressed(); +} + void PressButtonAndDebounce(logic::CommandBase &cb, uint8_t btnIndex) { hal::adc::SetADC(config::buttonsADCIndex, config::buttonADCLimits[btnIndex][0] + 1); while (!mb::buttons.ButtonPressed(btnIndex)) { diff --git a/tests/unit/logic/stubs/main_loop_stub.h b/tests/unit/logic/stubs/main_loop_stub.h index 4ac7de6..b3fad03 100644 --- a/tests/unit/logic/stubs/main_loop_stub.h +++ b/tests/unit/logic/stubs/main_loop_stub.h @@ -26,6 +26,8 @@ void EnsureActiveSlotIndex(uint8_t slot, modules::globals::FilamentLoadState loa void SetFINDAStateAndDebounce(bool press); bool SimulateUnloadToFINDA(uint32_t step, uint32_t fsOff, uint32_t findaOff); +bool SimulateFeedToFINDA(uint32_t step, uint32_t findaOn); +bool SimulateRetractFromFINDA(uint32_t step, uint32_t findaOff); void PressButtonAndDebounce(logic::CommandBase &cb, uint8_t btnIndex); void ClearButtons(logic::CommandBase &cb); diff --git a/tests/unit/logic/tool_change/test_tool_change.cpp b/tests/unit/logic/tool_change/test_tool_change.cpp index 29f818f..3f56490 100644 --- a/tests/unit/logic/tool_change/test_tool_change.cpp +++ b/tests/unit/logic/tool_change/test_tool_change.cpp @@ -14,25 +14,30 @@ #include "../../modules/stubs/stub_adc.h" +#include "../stubs/homing.h" #include "../stubs/main_loop_stub.h" #include "../stubs/stub_motion.h" +#include + using Catch::Matchers::Equals; +using namespace std::placeholders; #include "../helpers/helpers.ipp" +// needs to be a separate function otherwise gdb has issues setting breakpoints inside +bool FeedingToFindaStep(logic::CommandBase &tc, uint32_t step, uint32_t triggerAt) { + if (step == triggerAt) { // on specified stepNr make FINDA trigger + hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::high); + } else if (step >= triggerAt + config::findaDebounceMs + 2) { + REQUIRE(mf::finda.Pressed() == true); + } + return tc.TopLevelState() == ProgressCode::FeedingToFinda; +} + void FeedingToFinda(logic::ToolChange &tc, uint8_t toSlot, uint32_t triggerAt = 1000) { // feeding to finda - REQUIRE(WhileCondition( - tc, - [&](uint32_t step) -> bool { - if(step == triggerAt){ // on specified stepNr make FINDA trigger - hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::high); - } else if(step >= triggerAt + config::findaDebounceMs + 1){ - REQUIRE(mf::finda.Pressed() == true); - } - return tc.TopLevelState() == ProgressCode::FeedingToFinda; }, - 200000UL)); + REQUIRE(WhileCondition(tc, std::bind(FeedingToFindaStep, std::ref(tc), _1, triggerAt), 200'000UL)); REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, toSlot, toSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToBondtech)); } @@ -58,6 +63,8 @@ void CheckFinishedCorrectly(logic::ToolChange &tc, uint8_t toSlot) { void ToolChange(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) { ForceReinitAllAutomata(); + SetFINDAStateAndDebounce(true); + mfs::fsensor.ProcessMessage(true); EnsureActiveSlotIndex(fromSlot, mg::FilamentLoadState::InNozzle); // restart the automaton @@ -86,9 +93,11 @@ void NoToolChange(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) { ForceReinitAllAutomata(); // the filament is LOADED + SetFINDAStateAndDebounce(true); + mfs::fsensor.ProcessMessage(true); EnsureActiveSlotIndex(fromSlot, mg::FilamentLoadState::InNozzle); - REQUIRE(VerifyEnvironmentState(mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), toSlot, false, false, ml::off, ml::off)); + REQUIRE(VerifyEnvironmentState(mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), toSlot, true, false, ml::off, ml::off)); // restart the automaton tc.Reset(toSlot); @@ -162,19 +171,15 @@ TEST_CASE("tool_change::same_slot_just_unloaded_filament", "[tool_change]") { void ToolChangeFailLoadToFinda(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) { ForceReinitAllAutomata(); + SetFINDAStateAndDebounce(true); + mfs::fsensor.ProcessMessage(true); EnsureActiveSlotIndex(fromSlot, mg::FilamentLoadState::InNozzle); // restart the automaton tc.Reset(toSlot); - REQUIRE(WhileCondition( - tc, - [&](uint32_t step) -> bool { - if(step == 2000){ // on 2000th step make FINDA trigger - hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::low); - } - return tc.TopLevelState() == ProgressCode::UnloadingFilament; }, - 200000UL)); + REQUIRE(WhileCondition(tc, std::bind(SimulateUnloadToFINDA, _1, 100, 2'000), 200'000)); + REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 5000)); REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::AtPulley); @@ -316,3 +321,75 @@ TEST_CASE("tool_change::load_fail_FINDA_resolve_btnR_FINDA", "[tool_change]") { } } } + +void ToolChangeFailFSensor(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) { + using namespace std::placeholders; + ForceReinitAllAutomata(); + + SetFINDAStateAndDebounce(true); + mfs::fsensor.ProcessMessage(true); + EnsureActiveSlotIndex(fromSlot, mg::FilamentLoadState::InNozzle); + + // restart the automaton + tc.Reset(toSlot); + + REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament)); + // simulate unload to finda but fail the fsensor test + REQUIRE(WhileCondition(tc, std::bind(SimulateUnloadToFINDA, _1, 500'000, 10'000), 200'000)); + REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), fromSlot, false, false, ml::off, ml::blink0, ErrorCode::FSENSOR_DIDNT_SWITCH_OFF, ProgressCode::UnloadingFilament)); + REQUIRE(tc.unl.State() == ProgressCode::ERRWaitingForUser); +} + +void ToolChangeFailFSensorMiddleBtn(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) { + using namespace std::placeholders; + + // user pulls filament out from the fsensor and presses Retry + mfs::fsensor.ProcessMessage(false); + PressButtonAndDebounce(tc, mb::Middle); + REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), fromSlot, false, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament)); + REQUIRE(tc.unl.State() == ProgressCode::FeedingToFinda); // MMU must find out where the filament is FS is OFF, FINDA is OFF + + // both movables should have their homing flag invalidated + REQUIRE_FALSE(mi::idler.HomingValid()); + REQUIRE_FALSE(ms::selector.HomingValid()); + + // make FINDA trigger - Idler will rehome in this step, Selector must remain at its place + SimulateIdlerHoming(tc); + REQUIRE(mi::idler.HomingValid()); + REQUIRE_FALSE(ms::selector.HomingValid()); + // now trigger the FINDA + REQUIRE(WhileCondition(tc, std::bind(SimulateFeedToFINDA, _1, 100), 5000)); + REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, fromSlot, fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament)); + REQUIRE(tc.unl.State() == ProgressCode::RetractingFromFinda); + + // make FINDA switch off + REQUIRE(WhileCondition(tc, std::bind(SimulateRetractFromFINDA, _1, 100), 5000)); + REQUIRE(WhileCondition( + tc, [&](uint32_t) { return tc.unl.State() == ProgressCode::RetractingFromFinda; }, 50000)); + + // Selector will start rehoming at this stage - that was the error this test was to find + REQUIRE(VerifyState(tc, mg::FilamentLoadState::AtPulley, fromSlot, config::toolCount, false, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament)); + REQUIRE(tc.unl.State() == ProgressCode::DisengagingIdler); + SimulateSelectorHoming(tc); + + // Idler has probably engaged meanwhile, ignore its position check + REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 50000)); + REQUIRE(VerifyState2(tc, mg::FilamentLoadState::AtPulley, config::toolCount, fromSlot, false, false, toSlot, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToFinda)); + + // after that, perform a normal load + FeedingToFinda(tc, toSlot, 100); + FeedingToBondtech(tc, toSlot); + CheckFinishedCorrectly(tc, toSlot); +} + +TEST_CASE("tool_change::load_fail_FSensor_resolve_btnM", "[tool_change]") { + logic::ToolChange tc; + for (uint8_t fromSlot = 0; fromSlot < config::toolCount; ++fromSlot) { + for (uint8_t toSlot = 0; toSlot < config::toolCount; ++toSlot) { + if (fromSlot != toSlot) { + ToolChangeFailFSensor(tc, fromSlot, toSlot); + ToolChangeFailFSensorMiddleBtn(tc, fromSlot, toSlot); + } + } + } +}