Change the semantics of load filament

Load filament performs feed to FINDA and retract:
- engage idler
- feed normal to FINDA with config::feedToFinda distance until FINDA triggers
- retract normal and as soon FINDA un-triggers move back to PTFE config::cuttingEdgeToFindaMidpoint
- disengage the idler

That implied introducing another substate machine - RetractFromFinda, which does the opposite
of FeedToFinda while also checking for the FINDA switching off while retracting filament.

Still, ToolChange and CutFilament need fixing with this change
pull/126/head
D.R.racer 2021-09-27 08:43:56 +02:00 committed by DRracer
parent 01b2280ea6
commit 6426295e32
11 changed files with 119 additions and 50 deletions

View File

@ -7,6 +7,7 @@ target_sources(
feed_to_finda.cpp feed_to_finda.cpp
load_filament.cpp load_filament.cpp
no_command.cpp no_command.cpp
retract_from_finda.cpp
set_mode.cpp set_mode.cpp
tool_change.cpp tool_change.cpp
unload_filament.cpp unload_filament.cpp

View File

@ -32,29 +32,14 @@ bool FeedToFinda::Step() {
if (mf::finda.Pressed() || (feedPhaseLimited && mui::userInput.AnyEvent())) { // @@TODO probably also a command from the printer if (mf::finda.Pressed() || (feedPhaseLimited && mui::userInput.AnyEvent())) { // @@TODO probably also a command from the printer
mm::motion.AbortPlannedMoves(); // stop pushing filament mm::motion.AbortPlannedMoves(); // stop pushing filament
// FINDA triggered - that means it works and detected the filament tip // FINDA triggered - that means it works and detected the filament tip
state = UnloadBackToPTFE; state = OK;
mm::motion.PlanMove<mm::Pulley>(-config::cuttingEdgeToFindaMidpoint, config::pulleyFeedrate);
} else if (mm::motion.QueueEmpty()) { // all moves have been finished and FINDA didn't switch on } else if (mm::motion.QueueEmpty()) { // all moves have been finished and FINDA didn't switch on
state = Failed; state = Failed;
// @@TODO - shall we disengage the idler?
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0);
} }
} }
return false; return false;
case UnloadBackToPTFE:
if (mm::motion.QueueEmpty()) { // all moves have been finished
state = DisengagingIdler;
mi::idler.Disengage();
}
return false;
case DisengagingIdler:
if (!mi::idler.Engaged()) {
state = OK;
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
}
// @@TODO FINDA must be reported as OFF again as we are pulling the filament from it - is this correct?
return false;
case OK: case OK:
case Failed: case Failed:
default: default:

View File

@ -51,20 +51,22 @@ bool LoadFilament::StepInner() {
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0); // signal loading error ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0); // signal loading error
} else { } else {
state = ProgressCode::FeedingToBondtech; state = ProgressCode::RetractingFromFinda;
james.Reset(config::feedToBondtechMaxRetries); retract.Reset();
} }
} }
break; break;
case ProgressCode::FeedingToBondtech: case ProgressCode::RetractingFromFinda:
if (james.Step()) { // No, Mr. Bond, I expect you to FEED if (retract.Step()) {
switch (james.State()) { if (retract.State() == RetractFromFinda::Failed) {
case FeedToBondtech::Failed: state = ProgressCode::ERRDisengagingIdler;
error = ErrorCode::FINDA_DIDNT_SWITCH_OFF;
case FeedToBondtech::OK:
mm::motion.Disable(mm::Pulley);
mi::idler.Disengage(); mi::idler.Disengage();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0); // signal loading error
} else {
state = ProgressCode::DisengagingIdler; state = ProgressCode::DisengagingIdler;
mi::idler.Disengage();
} }
} }
break; break;

View File

@ -2,7 +2,7 @@
#include <stdint.h> #include <stdint.h>
#include "command_base.h" #include "command_base.h"
#include "feed_to_finda.h" #include "feed_to_finda.h"
#include "feed_to_bondtech.h" #include "retract_from_finda.h"
namespace logic { namespace logic {
@ -21,7 +21,7 @@ public:
private: private:
FeedToFinda feed; FeedToFinda feed;
FeedToBondtech james; // bond ;) RetractFromFinda retract;
}; };
/// The one and only instance of LoadFilament state machine in the FW /// The one and only instance of LoadFilament state machine in the FW

View File

@ -33,4 +33,5 @@ enum class ProgressCode : uint_fast8_t {
ReturningSelector, // P21 ReturningSelector, // P21
ParkingSelector, // P22 ParkingSelector, // P22
EjectingFilament, // P23 EjectingFilament, // P23
RetractingFromFinda, // P24
}; };

View File

@ -0,0 +1,42 @@
#include "retract_from_finda.h"
#include "../modules/finda.h"
#include "../modules/globals.h"
#include "../modules/idler.h"
#include "../modules/leds.h"
#include "../modules/motion.h"
namespace logic {
void RetractFromFinda::Reset() {
state = EngagingIdler;
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::blink0);
mi::idler.Engage(mg::globals.ActiveSlot());
}
bool RetractFromFinda::Step() {
switch (state) {
case EngagingIdler:
if (mi::idler.Engaged()) {
state = UnloadBackToPTFE;
mm::motion.PlanMove<mm::Pulley>(-config::cuttingEdgeToFindaMidpoint, config::pulleyFeedrate);
}
return false;
case UnloadBackToPTFE:
if (mm::motion.QueueEmpty()) { // all moves have been finished
if (!mf::finda.Pressed()) { // FINDA switched off correctly
state = OK;
} else { // FINDA didn't switch off
state = Failed;
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0);
}
}
return false;
case OK:
case Failed:
default:
return true;
}
}
} // namespace logic

View File

@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
namespace logic {
/// @brief Retract filament from FINDA to PTFE
///
/// Continuously pull filament by a fixed length (originally 600 steps) + verify FINDA is switched OFF
struct RetractFromFinda {
/// internal states of the state machine
enum {
EngagingIdler,
UnloadBackToPTFE,
OK,
Failed
};
inline RetractFromFinda()
: state(OK) {}
/// Restart the automaton
void Reset();
/// @returns true if the state machine finished its job, false otherwise
bool Step();
/// This method may be used to check the result of the automaton
/// @returns OK if everything went OK and FINDA triggered
/// @returns Failed if the FINDA didn't trigger
inline uint8_t State() const { return state; }
private:
uint8_t state;
};
} // namespace logic

View File

@ -63,26 +63,26 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
1500)); 1500));
// From now on the FINDA is reported as ON // From now on the FINDA is reported as ON
// unloading back to PTFE // // unloading back to PTFE
REQUIRE(ff.State() == FeedToFinda::UnloadBackToPTFE); // REQUIRE(ff.State() == FeedToFinda::UnloadBackToPTFE);
REQUIRE(WhileCondition( // REQUIRE(WhileCondition(
ff, // ff,
[&](int) { return ff.State() == FeedToFinda::UnloadBackToPTFE; }, // [&](int) { return ff.State() == FeedToFinda::UnloadBackToPTFE; },
5000)); // 5000));
// disengaging idler // // disengaging idler
REQUIRE(ff.State() == FeedToFinda::DisengagingIdler); // REQUIRE(ff.State() == FeedToFinda::DisengagingIdler);
REQUIRE(WhileCondition( // REQUIRE(WhileCondition(
ff, // ff,
[&](int) { return mi::idler.Engaged(); }, // [&](int) { return mi::idler.Engaged(); },
5000)); // 5000));
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5).v); // @@TODO constants // CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5).v); // @@TODO constants
CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v); CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0).v);
// state machine finished ok, the green LED should be on // state machine finished ok, the green LED should be blinking
REQUIRE(ff.State() == FeedToFinda::OK); REQUIRE(ff.State() == FeedToFinda::OK);
REQUIRE(ml::leds.Mode(mg::globals.ActiveSlot(), ml::green) == ml::off); REQUIRE(ml::leds.Mode(mg::globals.ActiveSlot(), ml::green) == ml::blink0);
REQUIRE(ff.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true REQUIRE(ff.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true
} }

View File

@ -5,6 +5,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/logic/feed_to_bondtech.cpp ${CMAKE_SOURCE_DIR}/src/logic/feed_to_bondtech.cpp
${CMAKE_SOURCE_DIR}/src/logic/feed_to_finda.cpp ${CMAKE_SOURCE_DIR}/src/logic/feed_to_finda.cpp
${CMAKE_SOURCE_DIR}/src/logic/load_filament.cpp ${CMAKE_SOURCE_DIR}/src/logic/load_filament.cpp
${CMAKE_SOURCE_DIR}/src/logic/retract_from_finda.cpp
${CMAKE_SOURCE_DIR}/src/modules/buttons.cpp ${CMAKE_SOURCE_DIR}/src/modules/buttons.cpp
${CMAKE_SOURCE_DIR}/src/modules/debouncer.cpp ${CMAKE_SOURCE_DIR}/src/modules/debouncer.cpp
${CMAKE_SOURCE_DIR}/src/modules/finda.cpp ${CMAKE_SOURCE_DIR}/src/modules/finda.cpp

View File

@ -57,23 +57,23 @@ void LoadFilamentSuccessful(uint8_t slot, logic::LoadFilament &lf) {
} }
return lf.TopLevelState() == ProgressCode::FeedingToFinda; }, return lf.TopLevelState() == ProgressCode::FeedingToFinda; },
5000)); 5000));
REQUIRE(VerifyState(lf, false, slot, slot, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToBondtech)); REQUIRE(VerifyState(lf, false, slot, slot, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::RetractingFromFinda));
// Stage 3 - feeding to bondtech // Stage 3 - retracting from finda
// we'll make a fsensor switch during the process // we'll assume the finda is working correctly here
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
lf, lf,
[&](int step) -> bool { [&](int step) -> bool {
if(step == 100){ // on 100th step make fsensor trigger if(step == 50){ // on 50th step make FINDA trigger
mfs::fsensor.ProcessMessage(true); hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::low);
} }
return lf.TopLevelState() == ProgressCode::FeedingToBondtech; }, return lf.TopLevelState() == ProgressCode::RetractingFromFinda; },
5000)); 5000));
REQUIRE(VerifyState(lf, false, slot, slot, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::DisengagingIdler)); REQUIRE(VerifyState(lf, false, slot, slot, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::DisengagingIdler));
// Stage 4 - disengaging idler // Stage 4 - disengaging idler
REQUIRE(WhileTopState(lf, ProgressCode::DisengagingIdler, idlerEngageDisengageMaxSteps)); REQUIRE(WhileTopState(lf, ProgressCode::DisengagingIdler, idlerEngageDisengageMaxSteps));
REQUIRE(VerifyState(lf, true, mi::Idler::IdleSlotIndex(), slot, true, ml::off, ml::off, ErrorCode::OK, ProgressCode::OK)); REQUIRE(VerifyState(lf, true, mi::Idler::IdleSlotIndex(), slot, false, ml::on, ml::off, ErrorCode::OK, ProgressCode::OK));
} }
TEST_CASE("load_filament::regular_load_to_slot_0-4", "[load_filament]") { TEST_CASE("load_filament::regular_load_to_slot_0-4", "[load_filament]") {

View File

@ -5,6 +5,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/logic/feed_to_bondtech.cpp ${CMAKE_SOURCE_DIR}/src/logic/feed_to_bondtech.cpp
${CMAKE_SOURCE_DIR}/src/logic/feed_to_finda.cpp ${CMAKE_SOURCE_DIR}/src/logic/feed_to_finda.cpp
${CMAKE_SOURCE_DIR}/src/logic/load_filament.cpp ${CMAKE_SOURCE_DIR}/src/logic/load_filament.cpp
${CMAKE_SOURCE_DIR}/src/logic/retract_from_finda.cpp
${CMAKE_SOURCE_DIR}/src/logic/tool_change.cpp ${CMAKE_SOURCE_DIR}/src/logic/tool_change.cpp
${CMAKE_SOURCE_DIR}/src/logic/unload_filament.cpp ${CMAKE_SOURCE_DIR}/src/logic/unload_filament.cpp
${CMAKE_SOURCE_DIR}/src/logic/unload_to_finda.cpp ${CMAKE_SOURCE_DIR}/src/logic/unload_to_finda.cpp