UnloadToFinda: update unit tests

pull/353/head
D.R.racer 2025-11-19 14:42:54 +01:00
parent 3bf5a1a0dc
commit 9aa9e0cc2b
4 changed files with 89 additions and 115 deletions

View File

@ -87,7 +87,10 @@ bool UnloadToFinda::Step() {
// we reached the end of move queue, but the FINDA didn't switch off
// two possible causes - grinded filament or malfunctioning FINDA
if (--maxTries) {
Reset(maxTries); // try again
// Ideally, the Idler shall rehome and then try again.
// That would auto-resolve errors caused by slipped or misaligned Idler
mi::idler.InvalidateHoming();
Reset(maxTries);
} else {
state = FailedFINDA;
}

View File

@ -79,19 +79,27 @@ void ForceReinitAllAutomata() {
mg::globals.Init();
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::AtPulley);
}
void HomeIdler() {
logic::NoCommand nc; // just a dummy instance which has an empty Step()
SimulateIdlerHoming(nc);
SimulateIdlerWaitForHomingValid(nc);
SimulateIdlerMoveToParkingPosition(nc);
}
void HomeSelector() {
logic::NoCommand nc; // just a dummy instance which has an empty Step()
SimulateSelectorHoming(nc);
SimulateSelectorWaitForHomingValid(nc);
SimulateSelectorWaitForReadyState(nc);
}
void HomeIdlerAndSelector() {
mi::idler.InvalidateHoming();
ms::selector.InvalidateHoming();
logic::NoCommand nc; // just a dummy instance which has an empty Step()
SimulateIdlerHoming(nc);
SimulateIdlerWaitForHomingValid(nc);
SimulateIdlerMoveToParkingPosition(nc);
HomeIdler();
SimulateSelectorHoming(nc);
SimulateSelectorWaitForHomingValid(nc);
SimulateSelectorWaitForReadyState(nc);
HomeSelector();
}
bool EnsureActiveSlotIndex(uint8_t slot, mg::FilamentLoadState loadState) {

View File

@ -1,6 +1,7 @@
#pragma once
#include "../../../../src/logic/command_base.h"
#include "../../../../src/modules/globals.h"
#include "../../../../src/modules/idler.h"
extern void main_loop();
extern void ForceReinitAllAutomata();
@ -44,3 +45,21 @@ static constexpr uint32_t selectorMoveMaxSteps = 40000UL;
void HomeIdlerAndSelector();
void SimulateErrDisengagingIdler(logic::CommandBase &cb, ErrorCode deferredEC);
template <typename T>
bool SimulateEngageIdlerFully(T &cb) {
return WhileCondition(
cb,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000);
}
template <typename T>
bool SimulateEngageIdlerPartially(T &cb) {
return WhileCondition(
cb,
[&](uint32_t) { return !mi::idler.PartiallyDisengaged(); },
5000);
}
void HomeIdler();

View File

@ -12,10 +12,12 @@
#include "../../../../src/modules/motion.h"
#include "../../../../src/modules/permanent_storage.h"
#include "../../../../src/modules/selector.h"
#include "../../../../src/modules/timebase.h"
#include "../../../../src/logic/unload_to_finda.h"
#include "../../modules/stubs/stub_adc.h"
#include "../../modules/stubs/stub_timebase.h"
#include "../stubs/main_loop_stub.h"
#include "../stubs/stub_motion.h"
@ -24,7 +26,7 @@ using namespace std::placeholders;
namespace ha = hal::adc;
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
void UnloadToFindaCommonSetup(logic::UnloadToFinda &ff, uint8_t retryAttempts) {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
@ -35,10 +37,8 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
// restart the automaton - just 1 attempt
ff.Reset(1);
ff.Reset(retryAttempts);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
@ -47,12 +47,10 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::IntermediateSlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
// engaging idler partially
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.PartiallyDisengaged(); },
5000));
REQUIRE(SimulateEngageIdlerPartially(ff));
}
void UnloadToFindaCommonTurnOffFSensor(logic::UnloadToFinda &ff) {
// turn off fsensor - the printer freed the filament from the gears
SetFSensorStateAndDebounce(false);
@ -63,14 +61,17 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
CHECK(mm::axes[mm::Pulley].enabled == true);
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
// engaging idler fully
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
REQUIRE(SimulateEngageIdlerFully(ff));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
}
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
logic::UnloadToFinda ff;
UnloadToFindaCommonSetup(ff, 1);
UnloadToFindaCommonTurnOffFSensor(ff);
REQUIRE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 10, 1000), 1100));
REQUIRE(ff.State() == logic::UnloadToFinda::OK);
@ -92,37 +93,9 @@ TEST_CASE("unload_to_finda::no_sense_FINDA_upon_start", "[unload_to_finda]") {
}
TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
// we need finda ON
SetFINDAStateAndDebounce(true);
// fsensor should be ON
SetFSensorStateAndDebounce(true);
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
// restart the automaton - just 1 attempt
ff.Reset(1);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
UnloadToFindaCommonSetup(ff, 1);
UnloadToFindaCommonTurnOffFSensor(ff);
// no changes to FINDA during unload - we'll pretend it never triggers
// but set FSensor correctly
@ -134,96 +107,67 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]")
}
TEST_CASE("unload_to_finda::unload_without_FSensor_trigger", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
// we need finda ON
SetFINDAStateAndDebounce(true);
// fsensor should be ON
SetFSensorStateAndDebounce(true);
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
// restart the automaton - just 1 attempt
ff.Reset(1);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
UnloadToFindaCommonSetup(ff, 1);
// no changes to FSensor during unload - we'll pretend it never triggers
// but set FINDA correctly
REQUIRE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 150000, 10000), 50000));
// time-out in 4 seconds
mt::IncMillis(4000);
main_loop();
ff.Step();
// no pulling actually starts, because the fsensor didn't turn off and the time-out elapsed
REQUIRE(ff.State() == logic::UnloadToFinda::FailedFSensor);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InNozzle);
}
TEST_CASE("unload_to_finda::unload_repeated", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
// we need finda ON
SetFINDAStateAndDebounce(true);
// fsensor should be ON
SetFSensorStateAndDebounce(true);
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
UnloadToFindaCommonSetup(ff, 2);
// restart the automaton - 2 attempts
ff.Reset(2);
UnloadToFindaCommonTurnOffFSensor(ff);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
// remember raw Pulley pos for tweaking the steps below
// because a 20mm (config::fsensorToNozzleAvoidGrindUnload)
// move is being executed while the Idler is fully engaging
// It is roughly -90 steps
// int32_t pulleySteppedAlready = mm::axes[config::Pulley].pos;
// no changes to FINDA during unload - we'll pretend it never triggers
// but set FSensor correctly
// In this case it is vital to correctly compute the amount of steps
// to make the unload state machine restart after the 1st attempt
uint32_t unlSteps = 1 + mm::unitToSteps<mm::P_pos_t>(config::maximumBowdenLength + config::feedToFinda + config::filamentMinLoadedToMMU);
// The number of steps must be more than what the state machine expects for FINDA to trigger.
uint32_t unlSteps = 1 + mm::unitToSteps<mm::P_pos_t>(
// standard fast move distance
config::maximumBowdenLength + config::feedToFinda + config::filamentMinLoadedToMMU
// slow start move distance
+ config::fsensorToNozzleAvoidGrindUnload);
// compensation
// + pulleySteppedAlready;
REQUIRE_FALSE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 10, 150000), unlSteps));
main_loop();
ff.Step();
REQUIRE_FALSE(mi::idler.HomingValid());
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
HomeIdler();
main_loop();
ff.Step();
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
SimulateEngageIdlerPartially(ff);
main_loop();
ff.Step();
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
SimulateEngageIdlerFully(ff);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
// make arbitrary amount of steps