From 974c1ba6db3ecef67430adc78e6abab17281ef40 Mon Sep 17 00:00:00 2001 From: "D.R.racer" Date: Wed, 30 Jun 2021 14:05:36 +0200 Subject: [PATCH] Verify Cut filament state machine + update unit tests --- src/logic/cut_filament.cpp | 44 ++++++------ src/logic/cut_filament.h | 1 + src/logic/progress_codes.h | 1 + .../logic/cut_filament/test_cut_filament.cpp | 67 ++++++++++--------- 4 files changed, 60 insertions(+), 53 deletions(-) diff --git a/src/logic/cut_filament.cpp b/src/logic/cut_filament.cpp index 79f368b..601060f 100644 --- a/src/logic/cut_filament.cpp +++ b/src/logic/cut_filament.cpp @@ -19,10 +19,11 @@ namespace mg = modules::globals; void CutFilament::Reset(uint8_t param) { error = ErrorCode::OK; + cutSlot = param; if (mg::globals.FilamentLoaded()) { state = ProgressCode::UnloadingFilament; - unl.Reset(mg::globals.ActiveSlot()); + unl.Reset(cutSlot); } else { SelectFilamentSlot(); } @@ -30,9 +31,8 @@ void CutFilament::Reset(uint8_t param) { void CutFilament::SelectFilamentSlot() { state = ProgressCode::SelectingFilamentSlot; - uint8_t newFilamentSlot = mg::globals.ActiveSlot() + 1; // move 1 slot aside - mi::idler.Engage(newFilamentSlot); //@@TODO does this make sense? - ms::selector.MoveToSlot(newFilamentSlot); + mi::idler.Engage(cutSlot); + ms::selector.MoveToSlot(cutSlot); } bool CutFilament::Step() { @@ -46,34 +46,32 @@ bool CutFilament::Step() { } break; case ProgressCode::SelectingFilamentSlot: - if (mm::motion.QueueEmpty()) { // idler and selector finished their moves + if (mi::idler.Engaged() && ms::selector.Slot() == cutSlot) { // idler and selector finished their moves feed.Reset(true); state = ProgressCode::FeedingToFinda; } break; case ProgressCode::FeedingToFinda: // @@TODO this state will be reused for repeated cutting of filament ... probably there will be multiple attempts, not sure - //@@TODO - this is not correct - when the active slot is +1, the FINDA cannot detect the incoming filament - we can only pray that the filament moves - //idler should hold slot 0, while the selector is at slot 1 if (feed.Step()) { if (feed.State() == FeedToFinda::Failed) { // @@TODO } else { - // move selector aside - prepare the blade into active position - state = ProgressCode::PreparingBlade; - ms::selector.MoveToSlot(mg::globals.ActiveSlot()); + // unload back to the pulley + state = ProgressCode::UnloadingToPulley; + mm::motion.PlanMove(mm::Pulley, -400, 1000); // @@TODO constants } } break; - case ProgressCode::PreparingBlade: - if (mm::motion.QueueEmpty()) { - state = ProgressCode::EngagingIdler; - mi::idler.Engage(mg::globals.ActiveSlot()); + case ProgressCode::UnloadingToPulley: + if (mm::motion.QueueEmpty()) { // idler and selector finished their moves + // move selector aside - prepare the blade into active position + state = ProgressCode::PreparingBlade; + ms::selector.MoveToSlot(cutSlot + 1); } - break; - case ProgressCode::EngagingIdler: - if (mi::idler.Engaged()) { + case ProgressCode::PreparingBlade: + if (ms::selector.Slot() == cutSlot + 1) { state = ProgressCode::PushingFilament; - mm::motion.PlanMove(cutStepsPre, 0, 0, 1500, 0, 0); //@@TODO + mm::motion.PlanMove(mm::Pulley, 400, 1000); // @@TODO constants } break; case ProgressCode::PushingFilament: @@ -83,17 +81,19 @@ bool CutFilament::Step() { } break; case ProgressCode::PerformingCut: - if (mm::motion.QueueEmpty()) { // this may not be necessary if we want the selector and pulley move at once + if (ms::selector.Slot() == 0) { // this may not be necessary if we want the selector and pulley move at once state = ProgressCode::ReturningSelector; - ms::selector.MoveToSlot(mg::globals.ActiveSlot()); // return selector back + ms::selector.MoveToSlot(5); // move selector to the other end of its axis to cleanup } break; case ProgressCode::ReturningSelector: - if (mm::motion.QueueEmpty()) { // selector returned to position, feed the filament back to FINDA - state = ProgressCode::FeedingToFinda; + if (ms::selector.Slot() == 5) { // selector returned to position, feed the filament back to FINDA + state = ProgressCode::OK; feed.Reset(true); } break; + case ProgressCode::OK: + return true; default: // we got into an unhandled state, better report it state = ProgressCode::ERRInternal; error = ErrorCode::INTERNAL; diff --git a/src/logic/cut_filament.h b/src/logic/cut_filament.h index 01f1758..d60ec96 100644 --- a/src/logic/cut_filament.h +++ b/src/logic/cut_filament.h @@ -28,6 +28,7 @@ private: constexpr static const uint16_t cutStepsPost = 150; UnloadFilament unl; ///< a high-level command/operation may be used as a building block of other operations as well FeedToFinda feed; + uint8_t cutSlot; void SelectFilamentSlot(); }; diff --git a/src/logic/progress_codes.h b/src/logic/progress_codes.h index 55883f6..e3d7e60 100644 --- a/src/logic/progress_codes.h +++ b/src/logic/progress_codes.h @@ -10,6 +10,7 @@ enum class ProgressCode : uint_fast8_t { EngagingIdler, DisengagingIdler, UnloadingToFinda, + UnloadingToPulley, FeedingToFinda, FeedingToBondtech, AvoidingGrind, diff --git a/tests/unit/logic/cut_filament/test_cut_filament.cpp b/tests/unit/logic/cut_filament/test_cut_filament.cpp index ffe465a..ed9456d 100644 --- a/tests/unit/logic/cut_filament/test_cut_filament.cpp +++ b/tests/unit/logic/cut_filament/test_cut_filament.cpp @@ -27,30 +27,29 @@ namespace mb = modules::buttons; namespace mg = modules::globals; namespace ms = modules::selector; +#include "../helpers/helpers.ipp" + TEST_CASE("cut_filament::cut0", "[cut_filament]") { - using namespace logic; + uint8_t cutSlot = 0; ForceReinitAllAutomata(); - CutFilament cf; + logic::CutFilament cf; + + EnsureActiveSlotIndex(cutSlot); + // restart the automaton - cf.Reset(0); + cf.Reset(cutSlot); - main_loop(); - - // it should have instructed the selector and idler to move to slot 1 - // check if the idler and selector have the right command - CHECK(modules::motion::axes[modules::motion::Idler].targetPos == mi::Idler::SlotPosition(0)); - CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(0)); + // check initial conditions + REQUIRE(VerifyState(cf, false, 5, cutSlot, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::SelectingFilamentSlot)); // now cycle at most some number of cycles (to be determined yet) and then verify, that the idler and selector reached their target positions REQUIRE(WhileTopState(cf, ProgressCode::SelectingFilamentSlot, 5000)); - CHECK(modules::motion::axes[modules::motion::Idler].pos == mi::Idler::SlotPosition(0)); - CHECK(modules::motion::axes[modules::motion::Selector].pos == ms::Selector::SlotPosition(0)); - // idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step - REQUIRE(cf.TopLevelState() == ProgressCode::FeedingToFinda); + REQUIRE(VerifyState(cf, false, cutSlot, cutSlot, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::FeedingToFinda)); + // prepare for simulated finda trigger REQUIRE(WhileCondition( cf, @@ -60,29 +59,35 @@ TEST_CASE("cut_filament::cut0", "[cut_filament]") { } return cf.TopLevelState() == ProgressCode::FeedingToFinda; }, 5000)); - // filament fed into FINDA, cutting... - REQUIRE(cf.TopLevelState() == ProgressCode::PreparingBlade); + // filament fed to FINDA + //@@TODO filament loaded flag - decide whether the filament loaded flag means really loaded into the printer or just a piece of filament + // stuck out of the pulley to prevent movement of the selector + REQUIRE(VerifyState(cf, /*true*/ false, cutSlot, cutSlot, true, ml::blink0, ml::off, ErrorCode::OK, ProgressCode::UnloadingToPulley)); + + // pull it back to the pulley + simulate FINDA depress + REQUIRE(WhileCondition( + cf, + [&](int step) -> bool { + if( step == 100 ){ // simulate FINDA trigger - will get depressed in 100 steps + hal::adc::SetADC(1, 0); + } + return cf.TopLevelState() == ProgressCode::UnloadingToPulley; }, 5000)); + + REQUIRE(VerifyState(cf, /*true*/ false, cutSlot, cutSlot, false, ml::blink0, ml::off, ErrorCode::OK, ProgressCode::PreparingBlade)); + + // now move the selector aside, prepare for cutting REQUIRE(WhileTopState(cf, ProgressCode::PreparingBlade, 5000)); + REQUIRE(VerifyState(cf, /*true*/ false, cutSlot, cutSlot + 1, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::PushingFilament)); - REQUIRE(cf.TopLevelState() == ProgressCode::EngagingIdler); - REQUIRE(WhileTopState(cf, ProgressCode::EngagingIdler, 5000)); - - // the idler should be at the active slot @@TODO - REQUIRE(cf.TopLevelState() == ProgressCode::PushingFilament); + // pushing filament a bit for a cut REQUIRE(WhileTopState(cf, ProgressCode::PushingFilament, 5000)); + REQUIRE(VerifyState(cf, /*true*/ false, cutSlot, cutSlot + 1, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::PerformingCut)); - // filament pushed - performing cut - REQUIRE(cf.TopLevelState() == ProgressCode::PerformingCut); + // cutting REQUIRE(WhileTopState(cf, ProgressCode::PerformingCut, 5000)); + REQUIRE(VerifyState(cf, /*true*/ false, cutSlot, 0, false, ml::blink0, ml::off, ErrorCode::OK, ProgressCode::ReturningSelector)); - // returning selector - REQUIRE(cf.TopLevelState() == ProgressCode::ReturningSelector); + // moving selector to the other end of its axis REQUIRE(WhileTopState(cf, ProgressCode::ReturningSelector, 5000)); - - // the next states are still @@TODO + REQUIRE(VerifyState(cf, /*true*/ false, cutSlot, 5, false, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK)); } - -// comments: -// The tricky part of the whole state machine are the edge cases - filament not loaded, stall guards etc. -// ... all the external influence we can get on the real HW -// But the good news is we can simulate them all in the unit test and thus ensure proper handling