Greatly improve unload filament unit test

+ relevant changes for other unit tests -> prepare for improving
of the other unit tests as well
pull/37/head
D.R.racer 2021-06-22 21:47:39 +02:00 committed by DRracer
parent 807eda7db3
commit ce20f0b001
20 changed files with 354 additions and 225 deletions

View File

@ -12,9 +12,6 @@ namespace logic {
ToolChange toolChange; ToolChange toolChange;
namespace mm = modules::motion;
namespace mi = modules::idler;
namespace ms = modules::selector;
namespace mg = modules::globals; namespace mg = modules::globals;
void ToolChange::Reset(uint8_t param) { void ToolChange::Reset(uint8_t param) {
@ -36,35 +33,51 @@ bool ToolChange::Step() {
switch (state) { switch (state) {
case ProgressCode::UnloadingFilament: case ProgressCode::UnloadingFilament:
if (unl.Step()) { if (unl.Step()) {
// unloading sequence finished // unloading sequence finished - basically, no errors can occurr here
switch (unl.Error()) { // as UnloadFilament should handle all the possible error states on its own
case ErrorCode::OK: // finished successfully // There is no way the UnloadFilament to finish in an error state
state = ProgressCode::LoadingFilament; state = ProgressCode::LoadingFilament;
load.Reset(plannedSlot); load.Reset(plannedSlot);
break;
case ErrorCode::UNLOAD_ERROR2: // @@TODO what shall we do in case of this error?
case ErrorCode::FINDA_DIDNT_TRIGGER:
break;
}
} }
break; break;
case ProgressCode::LoadingFilament: case ProgressCode::LoadingFilament:
if (load.Step()) { if (load.Step()) {
// unloading sequence finished // loading sequence finished - basically, no errors can occurr here
switch (load.Error()) { // as LoadFilament should handle all the possible error states on its own
case ErrorCode::OK: // finished successfully // There is no way the LoadFilament to finish in an error state
state = ProgressCode::OK; state = ProgressCode::OK;
break;
// case ErrorCode::LOAD_ERROR2: // @@TODO load errors?
// case ErrorCode::LOAD_FINDA_DIDNT_TRIGGER:
break;
}
} }
break; break;
case ProgressCode::OK: case ProgressCode::OK:
return true; return true;
default: // we got into an unhandled state, better report it
state = ProgressCode::ERRInternal;
error = ErrorCode::INTERNAL;
return true;
} }
return false; return false;
} }
ProgressCode ToolChange::State() const {
switch (state) {
case ProgressCode::UnloadingFilament:
return unl.State(); // report sub-automaton states properly
case ProgressCode::LoadingFilament:
return load.State(); // report sub-automaton states properly
default:
return state;
}
}
ErrorCode ToolChange::Error() const {
switch (state) {
case ProgressCode::UnloadingFilament:
return unl.Error(); // report sub-automaton errors properly
case ProgressCode::LoadingFilament:
return load.Error(); // report sub-automaton errors properly
default:
return error;
}
}
} // namespace logic } // namespace logic

View File

@ -19,6 +19,10 @@ public:
/// @returns true if the state machine finished its job, false otherwise /// @returns true if the state machine finished its job, false otherwise
bool Step() override; bool Step() override;
ProgressCode State() const override;
ErrorCode Error() const override;
private: private:
UnloadFilament unl; ///< a high-level command/operation may be used as a building block of other operations as well UnloadFilament unl; ///< a high-level command/operation may be used as a building block of other operations as well
LoadFilament load; LoadFilament load;

View File

@ -17,12 +17,14 @@ namespace mi = modules::idler;
namespace ml = modules::leds; namespace ml = modules::leds;
namespace mg = modules::globals; namespace mg = modules::globals;
void UnloadFilament::Reset(uint8_t param) { void UnloadFilament::Reset(uint8_t /*param*/) {
// unloads filament from extruder - filament is above Bondtech gears // unloads filament from extruder - filament is above Bondtech gears
mm::motion.InitAxis(mm::Pulley); mm::motion.InitAxis(mm::Pulley);
state = ProgressCode::UnloadingToFinda; state = ProgressCode::UnloadingToFinda;
error = ErrorCode::OK; error = ErrorCode::OK;
unl.Reset(maxRetries); unl.Reset(maxRetries);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::blink0);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::off);
} }
bool UnloadFilament::Step() { bool UnloadFilament::Step() {
@ -33,7 +35,9 @@ bool UnloadFilament::Step() {
if (unl.State() == UnloadToFinda::Failed) { if (unl.State() == UnloadToFinda::Failed) {
// couldn't unload to FINDA, report error and wait for user to resolve it // couldn't unload to FINDA, report error and wait for user to resolve it
state = ProgressCode::ERR1DisengagingIdler; state = ProgressCode::ERR1DisengagingIdler;
error = ErrorCode::FINDA_DIDNT_TRIGGER;
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0);
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
} else { } else {
state = ProgressCode::DisengagingIdler; state = ProgressCode::DisengagingIdler;
} }
@ -59,10 +63,11 @@ bool UnloadFilament::Step() {
if (mm::motion.QueueEmpty()) { if (mm::motion.QueueEmpty()) {
state = ProgressCode::OK; state = ProgressCode::OK;
mm::motion.DisableAxis(mm::Pulley); mm::motion.DisableAxis(mm::Pulley);
mg::globals.SetFilamentLoaded(false); // filament unloaded
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
} }
return false; return false;
case ProgressCode::ERR1DisengagingIdler: // couldn't unload to FINDA case ProgressCode::ERR1DisengagingIdler: // couldn't unload to FINDA
error = ErrorCode::FINDA_DIDNT_TRIGGER;
if (!mi::idler.Engaged()) { if (!mi::idler.Engaged()) {
state = ProgressCode::ERR1WaitingForUser; state = ProgressCode::ERR1WaitingForUser;
} }
@ -88,7 +93,6 @@ bool UnloadFilament::Step() {
return false; return false;
} }
case ProgressCode::OK: case ProgressCode::OK:
mg::globals.SetFilamentLoaded(false); // filament unloaded
return true; // successfully finished return true; // successfully finished
default: // we got into an unhandled state, better report it default: // we got into an unhandled state, better report it
state = ProgressCode::ERRInternal; state = ProgressCode::ERRInternal;

View File

@ -13,7 +13,8 @@ public:
: CommandBase() {} : CommandBase() {}
/// Restart the automaton /// Restart the automaton
void Reset(uint8_t param) override; /// @param param is not used, always unloads from the active slot
void Reset(uint8_t /*param*/) override;
/// @returns true if the state machine finished its job, false otherwise /// @returns true if the state machine finished its job, false otherwise
bool Step() override; bool Step() override;

View File

@ -15,7 +15,7 @@ void Debouncer::Step(uint16_t time, bool press) {
break; break;
case State::Detected: case State::Detected:
if (f.tmp == press) { if (f.tmp == press) {
if (time - timeLastChange > debounceTimeout) { if (time - timeLastChange >= debounceTimeout) {
f.state = State::WaitForRelease; f.state = State::WaitForRelease;
} }
} else { } else {

View File

@ -7,16 +7,15 @@ namespace finda {
class FINDA : protected debounce::Debouncer { class FINDA : protected debounce::Debouncer {
public: public:
inline constexpr FINDA()
: debounce::Debouncer(debounce) {};
void Step();
using debounce::Debouncer::Pressed;
private:
/// time interval for debouncing @@TODO specify units /// time interval for debouncing @@TODO specify units
constexpr static const uint16_t debounce = 100; constexpr static const uint16_t debounce = 100;
/// ADC decision level when a FINDA is considered pressed/not pressed /// ADC decision level when a FINDA is considered pressed/not pressed
constexpr static const uint16_t adcDecisionLevel = 512; constexpr static const uint16_t adcDecisionLevel = 512;
inline constexpr FINDA()
: debounce::Debouncer(debounce) {};
void Step();
using debounce::Debouncer::Pressed;
}; };
extern FINDA finda; extern FINDA finda;

View File

@ -34,7 +34,6 @@ TEST_CASE("cut_filament::cut0", "[cut_filament]") {
CutFilament cf; CutFilament cf;
// restart the automaton // restart the automaton
currentCommand = &cf;
cf.Reset(0); cf.Reset(0);
main_loop(); main_loop();
@ -45,7 +44,7 @@ TEST_CASE("cut_filament::cut0", "[cut_filament]") {
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(0)); CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(0));
// now cycle at most some number of cycles (to be determined yet) and then verify, that the idler and selector reached their target positions // 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(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::SelectingFilamentSlot; }, 5000)); REQUIRE(WhileTopState(cf, ProgressCode::SelectingFilamentSlot, 5000));
CHECK(modules::motion::axes[modules::motion::Idler].pos == mi::Idler::SlotPosition(0)); CHECK(modules::motion::axes[modules::motion::Idler].pos == mi::Idler::SlotPosition(0));
CHECK(modules::motion::axes[modules::motion::Selector].pos == ms::Selector::SlotPosition(0)); CHECK(modules::motion::axes[modules::motion::Selector].pos == ms::Selector::SlotPosition(0));
@ -53,27 +52,32 @@ TEST_CASE("cut_filament::cut0", "[cut_filament]") {
// idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step // 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(cf.TopLevelState() == ProgressCode::FeedingToFinda);
// prepare for simulated finda trigger // prepare for simulated finda trigger
hal::adc::ReinitADC(1, hal::adc::TADCData({ 0, 0, 0, 0, 600, 700, 800, 900 }), 10); REQUIRE(WhileCondition(
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::FeedingToFinda; }, 5000)); cf,
[&](int step) -> bool {
if( step == 1000 ){ // simulate FINDA trigger - will get pressed in 100 steps (due to debouncing)
hal::adc::SetADC(1, 900);
}
return cf.TopLevelState() == ProgressCode::FeedingToFinda; }, 5000));
// filament fed into FINDA, cutting... // filament fed into FINDA, cutting...
REQUIRE(cf.TopLevelState() == ProgressCode::PreparingBlade); REQUIRE(cf.TopLevelState() == ProgressCode::PreparingBlade);
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::PreparingBlade; }, 5000)); REQUIRE(WhileTopState(cf, ProgressCode::PreparingBlade, 5000));
REQUIRE(cf.TopLevelState() == ProgressCode::EngagingIdler); REQUIRE(cf.TopLevelState() == ProgressCode::EngagingIdler);
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::EngagingIdler; }, 5000)); REQUIRE(WhileTopState(cf, ProgressCode::EngagingIdler, 5000));
// the idler should be at the active slot @@TODO // the idler should be at the active slot @@TODO
REQUIRE(cf.TopLevelState() == ProgressCode::PushingFilament); REQUIRE(cf.TopLevelState() == ProgressCode::PushingFilament);
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::PushingFilament; }, 5000)); REQUIRE(WhileTopState(cf, ProgressCode::PushingFilament, 5000));
// filament pushed - performing cut // filament pushed - performing cut
REQUIRE(cf.TopLevelState() == ProgressCode::PerformingCut); REQUIRE(cf.TopLevelState() == ProgressCode::PerformingCut);
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::PerformingCut; }, 5000)); REQUIRE(WhileTopState(cf, ProgressCode::PerformingCut, 5000));
// returning selector // returning selector
REQUIRE(cf.TopLevelState() == ProgressCode::ReturningSelector); REQUIRE(cf.TopLevelState() == ProgressCode::ReturningSelector);
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::ReturningSelector; }, 5000)); REQUIRE(WhileTopState(cf, ProgressCode::ReturningSelector, 5000));
// the next states are still @@TODO // the next states are still @@TODO
} }

View File

@ -34,7 +34,6 @@ TEST_CASE("eject_filament::eject0", "[eject_filament]") {
EjectFilament ef; EjectFilament ef;
// restart the automaton // restart the automaton
currentCommand = &ef;
ef.Reset(0); ef.Reset(0);
main_loop(); main_loop();
@ -45,32 +44,32 @@ TEST_CASE("eject_filament::eject0", "[eject_filament]") {
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(4)); CHECK(modules::motion::axes[modules::motion::Selector].targetPos == ms::Selector::SlotPosition(4));
// now cycle at most some number of cycles (to be determined yet) and then verify, that the idler and selector reached their target positions // 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(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::SelectingFilamentSlot; }, 5000)); REQUIRE(WhileTopState(ef, ProgressCode::SelectingFilamentSlot, 5000));
// idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step // idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step
REQUIRE(ef.TopLevelState() == ProgressCode::FeedingToFinda); REQUIRE(ef.TopLevelState() == ProgressCode::FeedingToFinda);
// prepare for simulated finda trigger // prepare for simulated finda trigger
hal::adc::ReinitADC(1, hal::adc::TADCData({ 0, 0, 0, 0, 600, 700, 800, 900 }), 10); hal::adc::ReinitADC(1, hal::adc::TADCData({ 0, 0, 0, 0, 600, 700, 800, 900 }), 10);
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::FeedingToFinda; }, 50000)); REQUIRE(WhileTopState(ef, ProgressCode::FeedingToFinda, 50000));
// filament fed into FINDA, cutting... // filament fed into FINDA, cutting...
REQUIRE(ef.TopLevelState() == ProgressCode::PreparingBlade); REQUIRE(ef.TopLevelState() == ProgressCode::PreparingBlade);
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::PreparingBlade; }, 5000)); REQUIRE(WhileTopState(ef, ProgressCode::PreparingBlade, 5000));
REQUIRE(ef.TopLevelState() == ProgressCode::EngagingIdler); REQUIRE(ef.TopLevelState() == ProgressCode::EngagingIdler);
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::EngagingIdler; }, 5000)); REQUIRE(WhileTopState(ef, ProgressCode::EngagingIdler, 5000));
// the idler should be at the active slot @@TODO // the idler should be at the active slot @@TODO
REQUIRE(ef.TopLevelState() == ProgressCode::PushingFilament); REQUIRE(ef.TopLevelState() == ProgressCode::PushingFilament);
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::PushingFilament; }, 5000)); REQUIRE(WhileTopState(ef, ProgressCode::PushingFilament, 5000));
// filament pushed - performing cut // filament pushed - performing cut
REQUIRE(ef.TopLevelState() == ProgressCode::PerformingCut); REQUIRE(ef.TopLevelState() == ProgressCode::PerformingCut);
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::PerformingCut; }, 5000)); REQUIRE(WhileTopState(ef, ProgressCode::PerformingCut, 5000));
// returning selector // returning selector
REQUIRE(ef.TopLevelState() == ProgressCode::ReturningSelector); REQUIRE(ef.TopLevelState() == ProgressCode::ReturningSelector);
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::ReturningSelector; }, 5000)); REQUIRE(WhileTopState(ef, ProgressCode::ReturningSelector, 5000));
// the next states are still @@TODO // the next states are still @@TODO
} }

View File

@ -29,15 +29,6 @@ namespace ms = modules::selector;
namespace ha = hal::adc; namespace ha = hal::adc;
template <typename COND>
bool WhileCondition(logic::FeedToBondtech &ff, COND cond, uint32_t maxLoops = 5000) {
while (cond() && --maxLoops) {
main_loop();
ff.Step();
}
return maxLoops > 0;
}
TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
using namespace logic; using namespace logic;
@ -47,108 +38,50 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
main_loop(); main_loop();
// restart the automaton // restart the automaton
fb.Reset(false); fb.Reset(1);
// REQUIRE(ff.State() == FeedToBondtech::EngagingIdler); REQUIRE(fb.State() == FeedToBondtech::EngagingIdler);
// // it should have instructed the selector and idler to move to slot 1 // it should have instructed the selector and idler to move to slot 0
// // check if the idler and selector have the right command // check if the idler and selector have the right command
// CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0)); CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0));
// CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0)); CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0));
// CHECK(mm::axes[mm::Idler].enabled == true); CHECK(mm::axes[mm::Idler].enabled == true);
// // engaging idler // engaging idler
// REQUIRE(WhileCondition( REQUIRE(WhileCondition(
// ff, fb,
// [&]() { return !mi::idler.Engaged(); }, [&](int) { return !mi::idler.Engaged(); },
// 5000)); 5000));
// CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0));
// CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0));
// // idler engaged, selector in position, we'll start pushing filament // idler engaged, selector in position, we'll start pushing filament
// REQUIRE(ff.State() == FeedToBondtech::PushingFilament); REQUIRE(fb.State() == FeedToBondtech::PushingFilament);
// // at least at the beginning the LED should shine green (it should be blinking, but this mode has been already verified in the LED's unit test) // at least at the beginning the LED should shine green (it should be blinking, but this mode has been already verified in the LED's unit test)
// REQUIRE(ml::leds.LedOn(mg::globals.ActiveSlot(), ml::Color::green)); REQUIRE(ml::leds.LedOn(mg::globals.ActiveSlot(), ml::Color::green));
// // now let the filament be pushed into the FINDA - do 500 steps without triggering the condition // @@TODO simulate incoming message from the printer - fsensor triggered
// // and then let the simulated ADC channel 1 create a FINDA switch
// ha::ReinitADC(1, ha::TADCData({ 600, 700, 800, 900 }), 1);
// REQUIRE(WhileCondition( REQUIRE(WhileCondition(
// ff, fb,
// [&]() { return ff.State() == FeedToBondtech::PushingFilament; }, [&](int) { return fb.State() == FeedToBondtech::PushingFilament; },
// 1500)); 1500));
// // From now on the FINDA is reported as ON
// // unloading back to PTFE // disengaging idler
// REQUIRE(ff.State() == FeedToBondtech::UnloadBackToPTFE); REQUIRE(fb.State() == FeedToBondtech::DisengagingIdler);
// REQUIRE(WhileCondition( REQUIRE(WhileCondition(
// ff, fb,
// [&]() { return ff.State() == FeedToBondtech::UnloadBackToPTFE; }, [&](int) { return fb.State() == FeedToBondtech::DisengagingIdler; },
// 5000)); 5000));
// // disengaging idler CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // @@TODO constants
// REQUIRE(ff.State() == FeedToBondtech::DisengagingIdler); CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0));
// REQUIRE(WhileCondition(
// ff,
// [&]() { return mi::idler.Engaged(); },
// 5000));
// CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // @@TODO constants // state machine finished ok, the green LED should be on
// CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0)); REQUIRE(fb.State() == FeedToBondtech::OK);
REQUIRE(ml::leds.LedOn(mg::globals.ActiveSlot(), ml::Color::green));
// // state machine finished ok, the green LED should be on REQUIRE(fb.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true
// REQUIRE(ff.State() == FeedToBondtech::OK);
// REQUIRE(ml::leds.LedOn(mg::globals.ActiveSlot(), ml::Color::green));
// REQUIRE(ff.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true
//}
//TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
// using namespace logic;
// ForceReinitAllAutomata();
// FeedToBondtech ff;
// main_loop();
// // restart the automaton - we want the limited version of the feed
// ff.Reset(true);
// REQUIRE(ff.State() == FeedToBondtech::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::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0));
// CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0));
// // engaging idler
// REQUIRE(WhileCondition(
// ff,
// [&]() { return !mi::idler.Engaged(); },
// 5000));
// CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0));
// CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0));
// // idler engaged, we'll start pushing filament
// REQUIRE(ff.State() == FeedToBondtech::PushingFilament);
// // at least at the beginning the LED should shine green (it should be blinking, but this mode has been already verified in the LED's unit test)
// REQUIRE(ml::leds.Mode(mg::globals.ActiveSlot(), ml::Color::green) == ml::blink0);
// // now let the filament be pushed into the FINDA - but we make sure the FINDA doesn't trigger at all
// ha::ReinitADC(1, ha::TADCData({ 0 }), 100);
// REQUIRE(WhileCondition(
// ff, // boo, this formatting is UGLY!
// [&]() { return ff.State() == FeedToBondtech::PushingFilament; },
// 5000));
// // the FINDA didn't trigger, we should be in the Failed state
// REQUIRE(ff.State() == FeedToBondtech::Failed);
// REQUIRE(ml::leds.Mode(mg::globals.ActiveSlot(), ml::Color::green) == ml::off);
// REQUIRE(ml::leds.Mode(mg::globals.ActiveSlot(), ml::Color::red) == ml::blink0);
// REQUIRE(ff.Step() == true); // the automaton finished its work, any consecutive calls to Step must return true
} }

View File

@ -29,15 +29,6 @@ namespace ms = modules::selector;
namespace ha = hal::adc; namespace ha = hal::adc;
template <typename COND>
bool WhileCondition(logic::FeedToFinda &ff, COND cond, uint32_t maxLoops = 5000) {
while (cond() && --maxLoops) {
main_loop();
ff.Step();
}
return maxLoops > 0;
}
TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") { TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
using namespace logic; using namespace logic;
@ -60,7 +51,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
// engaging idler // engaging idler
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
ff, ff,
[&]() { return !mi::idler.Engaged(); }, [&](int) { return !mi::idler.Engaged(); },
5000)); 5000));
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0));
@ -77,7 +68,7 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
ff, ff,
[&]() { return ff.State() == FeedToFinda::PushingFilament; }, [&](int) { return ff.State() == FeedToFinda::PushingFilament; },
1500)); 1500));
// From now on the FINDA is reported as ON // From now on the FINDA is reported as ON
@ -85,14 +76,14 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
REQUIRE(ff.State() == FeedToFinda::UnloadBackToPTFE); REQUIRE(ff.State() == FeedToFinda::UnloadBackToPTFE);
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
ff, ff,
[&]() { 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,
[&]() { return mi::idler.Engaged(); }, [&](int) { return mi::idler.Engaged(); },
5000)); 5000));
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // @@TODO constants CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // @@TODO constants
@ -126,7 +117,7 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
// engaging idler // engaging idler
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
ff, ff,
[&]() { return !mi::idler.Engaged(); }, [&](int) { return !mi::idler.Engaged(); },
5000)); 5000));
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0)); CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(0));
@ -141,8 +132,8 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
ha::ReinitADC(1, ha::TADCData({ 0 }), 100); ha::ReinitADC(1, ha::TADCData({ 0 }), 100);
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
ff, // boo, this formatting is UGLY! ff,
[&]() { return ff.State() == FeedToFinda::PushingFilament; }, [&](int) { return ff.State() == FeedToFinda::PushingFilament; },
5000)); 5000));
// the FINDA didn't trigger, we should be in the Failed state // the FINDA didn't trigger, we should be in the Failed state

View File

@ -34,7 +34,6 @@ TEST_CASE("unload_filament::unload0", "[unload_filament]") {
LoadFilament lf; LoadFilament lf;
// restart the automaton // restart the automaton
currentCommand = &lf;
lf.Reset(0); lf.Reset(0);
main_loop(); main_loop();
@ -53,4 +52,5 @@ TEST_CASE("unload_filament::unload0", "[unload_filament]") {
// REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::FinishingMoves; }, 5000)); // REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::FinishingMoves; }, 5000));
// REQUIRE(uf.TopLevelState() == ProgressCode::OK); // REQUIRE(uf.TopLevelState() == ProgressCode::OK);
REQUIRE(modules::globals::globals.FilamentLoaded() == true);
} }

View File

@ -13,9 +13,9 @@
#include "../../../../src/modules/permanent_storage.h" #include "../../../../src/modules/permanent_storage.h"
#include "../../../../src/modules/selector.h" #include "../../../../src/modules/selector.h"
#include <new> // bring in placement new #include "../stubs/stub_motion.h"
logic::CommandBase *currentCommand = nullptr; #include <new> // bring in placement new
void main_loop() { void main_loop() {
modules::buttons::buttons.Step(); modules::buttons::buttons.Step();
@ -25,8 +25,6 @@ void main_loop() {
modules::idler::idler.Step(); modules::idler::idler.Step();
modules::selector::selector.Step(); modules::selector::selector.Step();
modules::motion::motion.Step(); modules::motion::motion.Step();
if (currentCommand)
currentCommand->Step();
modules::time::IncMillis(); modules::time::IncMillis();
} }
@ -57,8 +55,12 @@ void ForceReinitAllAutomata() {
// finda OFF // finda OFF
hal::adc::ReinitADC(1, hal::adc::TADCData({ 0 }), 1); hal::adc::ReinitADC(1, hal::adc::TADCData({ 0 }), 1);
// reinit timing
modules::time::ReinitTimebase(); modules::time::ReinitTimebase();
// reinit axes positions
modules::motion::ReinitMotion();
// let's assume we have the filament NOT loaded and active slot 0 // let's assume we have the filament NOT loaded and active slot 0
modules::globals::globals.SetFilamentLoaded(false); modules::globals::globals.SetFilamentLoaded(false);
modules::globals::globals.SetActiveSlot(0); modules::globals::globals.SetActiveSlot(0);

View File

@ -4,12 +4,17 @@
extern void main_loop(); extern void main_loop();
extern void ForceReinitAllAutomata(); extern void ForceReinitAllAutomata();
extern logic::CommandBase *currentCommand; template <typename SM, typename COND>
bool WhileCondition(SM &sm, COND cond, uint32_t maxLoops = 5000) {
template <typename COND> while (cond(maxLoops) && --maxLoops) {
bool WhileCondition(COND cond, uint32_t maxLoops = 5000) {
while (cond() && --maxLoops) {
main_loop(); main_loop();
sm.Step();
} }
return maxLoops > 0; return maxLoops > 0;
} }
template <typename SM>
bool WhileTopState(SM &sm, ProgressCode state, uint32_t maxLoops = 5000) {
return WhileCondition(
sm, [&](int) { return sm.TopLevelState() == state; }, maxLoops);
}

View File

@ -5,10 +5,13 @@ namespace modules {
namespace motion { namespace motion {
Motion motion; Motion motion;
// Intentionally inited with strange values
// Need to call ReinitMotion() each time we start some unit test
AxisSim axes[3] = { AxisSim axes[3] = {
{ 0, 0, false, false, false }, // pulley { -32767, -32767, false, false, false }, // pulley
{ 1, 1, false, false, false }, // selector //@@TODO proper selector positions once defined { -32767, -32767, false, false, false }, // selector //@@TODO proper selector positions once defined
{ 0, 0, false, false, false }, // idler { -32767, -32767, false, false, false }, // idler
}; };
void Motion::InitAxis(Axis axis) { void Motion::InitAxis(Axis axis) {
@ -72,6 +75,13 @@ void Motion::AbortPlannedMoves() {
} }
} }
void ReinitMotion() {
// reset the simulation data to defaults
axes[0] = AxisSim({ 0, 0, false, false, false }); // pulley
axes[1] = AxisSim({ 1, 1, false, false, false }); // selector //@@TODO proper selector positions once defined
axes[2] = AxisSim({ 0, 0, false, false, false }); // idler
}
/// probably higher-level operations knowing the semantic meaning of axes /// probably higher-level operations knowing the semantic meaning of axes
} // namespace motion } // namespace motion

View File

@ -14,5 +14,7 @@ struct AxisSim {
extern AxisSim axes[3]; extern AxisSim axes[3];
extern void ReinitMotion();
} // namespace motion } // namespace motion
} // namespace modules } // namespace modules

View File

@ -34,23 +34,16 @@ TEST_CASE("tool_change::test0", "[tool_change]") {
ToolChange tc; ToolChange tc;
// restart the automaton // restart the automaton
currentCommand = &tc;
tc.Reset(0); tc.Reset(0);
main_loop(); main_loop();
// REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::UnloadingToFinda; }, 5000)); REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 5000));
REQUIRE(modules::globals::globals.FilamentLoaded() == false);
// REQUIRE(uf.TopLevelState() == ProgressCode::DisengagingIdler); REQUIRE(tc.TopLevelState() == ProgressCode::LoadingFilament);
// REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::DisengagingIdler; }, 5000)); REQUIRE(WhileTopState(tc, ProgressCode::LoadingFilament, 5000));
// CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); REQUIRE(tc.TopLevelState() == ProgressCode::OK);
REQUIRE(modules::globals::globals.FilamentLoaded() == true);
// REQUIRE(uf.TopLevelState() == ProgressCode::AvoidingGrind);
// REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::AvoidingGrind; }, 5000));
// REQUIRE(uf.TopLevelState() == ProgressCode::FinishingMoves);
// REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::FinishingMoves; }, 5000));
// REQUIRE(uf.TopLevelState() == ProgressCode::OK);
} }

View File

@ -27,30 +27,198 @@ namespace mb = modules::buttons;
namespace mg = modules::globals; namespace mg = modules::globals;
namespace ms = modules::selector; namespace ms = modules::selector;
TEST_CASE("unload_filament::unload0", "[unload_filament]") { void RegularUnloadFromSlot04(uint8_t slot) {
using namespace logic; // prepare startup conditions
ForceReinitAllAutomata(); ForceReinitAllAutomata();
UnloadFilament uf; // change the startup to what we need here
// move selector to the right spot
ms::selector.MoveToSlot(slot);
while (ms::selector.Slot() != slot)
main_loop();
mg::globals.SetActiveSlot(slot);
mg::globals.SetFilamentLoaded(true);
// set FINDA ON + debounce
hal::adc::SetADC(1, mf::FINDA::adcDecisionLevel + 1);
for (size_t i = 0; i < mf::FINDA::debounce + 1; ++i)
main_loop();
// verify startup conditions
REQUIRE(mg::globals.FilamentLoaded() == true);
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5));
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot));
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == true);
// restart the automaton // restart the automaton
currentCommand = &uf; logic::UnloadFilament uf;
uf.Reset(0); uf.Reset(slot);
main_loop(); // Stage 0 - verify state just after Reset()
REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // idler should have been activated by the underlying automaton
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == true); // FINDA triggered off
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::blink0); // green LED should blink
REQUIRE(uf.Error() == ErrorCode::OK); // no error so far
REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::UnloadingToFinda; }, 5000)); // run the automaton
// Stage 1 - unloading to FINDA
REQUIRE(WhileCondition(
uf,
[&](int step) -> bool {
if(step == 100){ // on 100th step make FINDA trigger
hal::adc::SetADC(1, 0);
}
return uf.TopLevelState() == ProgressCode::UnloadingToFinda; },
5000));
REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(slot)); // idler should have been activated by the underlying automaton
REQUIRE(mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == false); // FINDA triggered off
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::blink0); // green LED should blink
REQUIRE(uf.Error() == ErrorCode::OK); // no error so far
// Stage 2 - idler was engaged, disengage it
REQUIRE(uf.TopLevelState() == ProgressCode::DisengagingIdler); REQUIRE(uf.TopLevelState() == ProgressCode::DisengagingIdler);
REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::DisengagingIdler; }, 5000)); REQUIRE(WhileTopState(uf, ProgressCode::DisengagingIdler, 5000));
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // idler should have been disengaged
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == false); // FINDA still triggered off
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::blink0); // green LED should blink
REQUIRE(uf.Error() == ErrorCode::OK); // no error so far
// Stage 3 - avoiding grind (whatever is that @@TODO)
REQUIRE(uf.TopLevelState() == ProgressCode::AvoidingGrind); REQUIRE(uf.TopLevelState() == ProgressCode::AvoidingGrind);
REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::AvoidingGrind; }, 5000)); REQUIRE(WhileTopState(uf, ProgressCode::AvoidingGrind, 5000));
REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // idler should have been disengaged
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == false); // FINDA still triggered off
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::blink0); // green LED should blink
REQUIRE(uf.Error() == ErrorCode::OK); // no error so far
// Stage 4 - finishing moves and setting global state correctly
REQUIRE(uf.TopLevelState() == ProgressCode::FinishingMoves); REQUIRE(uf.TopLevelState() == ProgressCode::FinishingMoves);
REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::FinishingMoves; }, 5000)); REQUIRE(WhileTopState(uf, ProgressCode::FinishingMoves, 5000));
REQUIRE(mg::globals.FilamentLoaded() == false); // filament unloaded
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // idler should have been disengaged
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == false); // FINDA still triggered off
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::on); // green LED should be ON
REQUIRE(uf.Error() == ErrorCode::OK); // no error so far
// Stage 5 - repeated calls to TopLevelState should return "OK"
REQUIRE(uf.TopLevelState() == ProgressCode::OK); REQUIRE(uf.TopLevelState() == ProgressCode::OK);
REQUIRE(mg::globals.FilamentLoaded() == false);
REQUIRE(mf::finda.Pressed() == false);
REQUIRE(uf.Error() == ErrorCode::OK); // no error
}
TEST_CASE("unload_filament::regular_unload_from_slot_0-4", "[unload_filament]") {
for (uint8_t slot = 0; slot < 5; ++slot) {
RegularUnloadFromSlot04(slot);
}
}
void FindaDidntTrigger(uint8_t slot) {
// prepare startup conditions
ForceReinitAllAutomata();
// change the startup to what we need here
// move selector to the right spot
ms::selector.MoveToSlot(slot);
while (ms::selector.Slot() != slot)
main_loop();
// set FINDA ON + debounce
hal::adc::SetADC(1, mf::FINDA::adcDecisionLevel + 1);
for (size_t i = 0; i < mf::FINDA::debounce + 1; ++i)
main_loop();
mg::globals.SetActiveSlot(slot);
mg::globals.SetFilamentLoaded(true);
// verify startup conditions
REQUIRE(mg::globals.FilamentLoaded() == true);
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5));
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot));
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == true);
// restart the automaton
logic::UnloadFilament uf;
uf.Reset(slot);
// Stage 0 - verify state just after Reset()
REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // idler should have been activated by the underlying automaton
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == true); // FINDA triggered off
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::blink0); // green LED should blink
REQUIRE(uf.Error() == ErrorCode::OK); // no error so far
// run the automaton
// Stage 1 - unloading to FINDA - do NOT let it trigger - keep it pressed, the automaton should finish all moves with the pulley
// without reaching the FINDA and report an error
REQUIRE(WhileTopState(uf, ProgressCode::UnloadingToFinda, 50000));
REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(slot)); // idler should have been activated by the underlying automaton
REQUIRE(mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == true); // FINDA still on
REQUIRE(ml::leds.Mode(slot, ml::red) == ml::blink0); // red LED should blink
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::off); // green LED should be off
REQUIRE(uf.Error() == ErrorCode::FINDA_DIDNT_TRIGGER); // didn't get any response from FINDA
REQUIRE(uf.TopLevelState() == ProgressCode::ERR1DisengagingIdler);
// Stage 2 - idler should get disengaged
REQUIRE(WhileTopState(uf, ProgressCode::ERR1DisengagingIdler, 5000));
REQUIRE(mg::globals.FilamentLoaded() == true); // we still think we have filament loaded at this stage
REQUIRE(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // idler should have been disengaged
REQUIRE(!mi::idler.Engaged());
REQUIRE(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(slot)); // no change in selector's position
REQUIRE(ms::selector.Slot() == slot);
REQUIRE(mf::finda.Pressed() == true); // FINDA still on
REQUIRE(ml::leds.Mode(slot, ml::red) == ml::blink0); // red LED should blink
REQUIRE(ml::leds.Mode(slot, ml::green) == ml::off); // green LED should be off
REQUIRE(uf.Error() == ErrorCode::FINDA_DIDNT_TRIGGER);
REQUIRE(uf.TopLevelState() == ProgressCode::ERR1WaitingForUser);
// Stage 3 - the user has to do something
// there are 3 options:
// - help the filament a bit
// - try again the whole sequence
// - resolve the problem by hand - after pressing the button we shall check, that FINDA is off and we should do what?
}
TEST_CASE("unload_filament::finda_didnt_trigger", "[unload_filament]") {
for (uint8_t slot = 0; slot < 5; ++slot) {
FindaDidntTrigger(slot);
}
} }

View File

@ -29,15 +29,6 @@ namespace ms = modules::selector;
namespace ha = hal::adc; namespace ha = hal::adc;
template <typename COND>
bool WhileConditionFF(logic::UnloadToFinda &ff, COND cond, uint32_t maxLoops = 5000) {
while (cond() && --maxLoops) {
main_loop();
ff.Step();
}
return maxLoops > 0;
}
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") { TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
using namespace logic; using namespace logic;
@ -50,7 +41,8 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
// wait for FINDA to debounce // wait for FINDA to debounce
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
[&]() { return !mf::finda.Pressed(); }, ff,
[&](int) { return !mf::finda.Pressed(); },
5000)); 5000));
// restart the automaton - just 1 attempt // restart the automaton - just 1 attempt
@ -65,17 +57,17 @@ TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
CHECK(mm::axes[mm::Idler].enabled == true); CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler // engaging idler
REQUIRE(WhileConditionFF( REQUIRE(WhileCondition(
ff, ff,
[&]() { return !mi::idler.Engaged(); }, [&](int) { return !mi::idler.Engaged(); },
5000)); 5000));
// now pulling the filament until finda triggers // now pulling the filament until finda triggers
REQUIRE(ff.State() == UnloadToFinda::WaitingForFINDA); REQUIRE(ff.State() == UnloadToFinda::WaitingForFINDA);
hal::adc::ReinitADC(1, hal::adc::TADCData({ 1023, 900, 800, 500, 0 }), 10); hal::adc::ReinitADC(1, hal::adc::TADCData({ 1023, 900, 800, 500, 0 }), 10);
REQUIRE(WhileConditionFF( REQUIRE(WhileCondition(
ff, ff,
[&]() { return mf::finda.Pressed(); }, [&](int) { return mf::finda.Pressed(); },
50000)); 50000));
REQUIRE(ff.State() == UnloadToFinda::OK); REQUIRE(ff.State() == UnloadToFinda::OK);
@ -108,7 +100,8 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]")
// wait for FINDA to debounce // wait for FINDA to debounce
REQUIRE(WhileCondition( REQUIRE(WhileCondition(
[&]() { return !mf::finda.Pressed(); }, ff,
[&](int) { return !mf::finda.Pressed(); },
5000)); 5000));
// restart the automaton - just 1 attempt // restart the automaton - just 1 attempt
@ -123,18 +116,18 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]")
CHECK(mm::axes[mm::Idler].enabled == true); CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler // engaging idler
REQUIRE(WhileConditionFF( REQUIRE(WhileCondition(
ff, ff,
[&]() { return !mi::idler.Engaged(); }, [&](int) { return !mi::idler.Engaged(); },
5000)); 5000));
// now pulling the filament until finda triggers // now pulling the filament until finda triggers
REQUIRE(ff.State() == UnloadToFinda::WaitingForFINDA); REQUIRE(ff.State() == UnloadToFinda::WaitingForFINDA);
// no changes to FINDA during unload - we'll pretend it never triggers // no changes to FINDA during unload - we'll pretend it never triggers
REQUIRE(!WhileConditionFF( REQUIRE(!WhileCondition(
ff, ff,
[&]() { return mf::finda.Pressed(); }, [&](int) { return mf::finda.Pressed(); },
50000)); 50000));
REQUIRE(ff.State() == UnloadToFinda::Failed); REQUIRE(ff.State() == UnloadToFinda::Failed);

View File

@ -29,5 +29,9 @@ uint16_t ReadADC(uint8_t adc) {
return rdptr[adc] != values2Return[adc].end() ? *rdptr[adc] : values2Return[adc].back(); return rdptr[adc] != values2Return[adc].end() ? *rdptr[adc] : values2Return[adc].back();
} }
void SetADC(uint8_t channel, uint16_t value) {
ReinitADC(channel, TADCData({ value }), 1);
}
} // namespace adc } // namespace adc
} // namespace hal } // namespace hal

View File

@ -8,7 +8,11 @@ namespace adc {
using TADCData = std::vector<uint16_t>; using TADCData = std::vector<uint16_t>;
/// plan a vector of ADC values for the next steps
void ReinitADC(uint8_t channel, TADCData &&d, uint8_t ovsmpl); void ReinitADC(uint8_t channel, TADCData &&d, uint8_t ovsmpl);
/// set ADC value on a channel to some fixed value from now on
void SetADC(uint8_t channel, uint16_t value);
} // namespace adc } // namespace adc
} // namespace hal } // namespace hal