Add more checks (esp. positions of idler and selector)
that resulted in finding several weak spots and now Cut and Eject filament do not pass the test (which is correct, they really have logical issues)pull/30/head
parent
317a486d1e
commit
4d6d6fe0af
|
|
@ -36,9 +36,20 @@ public:
|
||||||
/// @returns progress of operation - each automaton consists of several internal states
|
/// @returns progress of operation - each automaton consists of several internal states
|
||||||
/// which should be reported to the user via the printer's LCD
|
/// which should be reported to the user via the printer's LCD
|
||||||
/// E.g. Tool change: first tries to unload filament, then selects another slot and then tries to load filament
|
/// E.g. Tool change: first tries to unload filament, then selects another slot and then tries to load filament
|
||||||
|
///
|
||||||
|
/// Beware - derived automata report detailed states of underlying state machines if any
|
||||||
|
/// E.g. Eject filament first tries to unload filament, which is a standalone automaton.
|
||||||
|
/// Therefore until the unload is finished, this method will report the internal state of Unload filament.
|
||||||
|
/// The reason for this is to be able to report exactly what is happening to the printer, especially loading and unloading sequences (and errors)
|
||||||
virtual ProgressCode State() const { return state; }
|
virtual ProgressCode State() const { return state; }
|
||||||
|
|
||||||
/// @returns status of the operation - e.g. RUNNING, OK, or an error code if the operation failed
|
/// @returns progress of operation of only this state machine - regardless of any underlying automata (if any)
|
||||||
|
/// Therefore it is not a vitual method.
|
||||||
|
ProgressCode TopLevelState() const { return state; }
|
||||||
|
|
||||||
|
/// @returns status of the operation - e.g. RUNNING, OK, or an error code if the operation failed.
|
||||||
|
///
|
||||||
|
/// Beware - the same rule about composite operations as with State() applies to Error() as well.
|
||||||
/// Please see @ErrorCode for more details
|
/// Please see @ErrorCode for more details
|
||||||
virtual ErrorCode Error() const { return error; }
|
virtual ErrorCode Error() const { return error; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,22 +36,13 @@ void CutFilament::SelectFilamentSlot() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CutFilament::Step() {
|
bool CutFilament::Step() {
|
||||||
const int cut_steps_pre = 700;
|
|
||||||
const int cut_steps_post = 150;
|
|
||||||
|
|
||||||
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
|
||||||
case ErrorCode::UNLOAD_ERROR2: // @@TODO what shall we do in case of this error?
|
SelectFilamentSlot();
|
||||||
case ErrorCode::FINDA_DIDNT_TRIGGER:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
state = ProgressCode::ERRInternal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ProgressCode::SelectingFilamentSlot:
|
case ProgressCode::SelectingFilamentSlot:
|
||||||
|
|
@ -61,6 +52,8 @@ bool CutFilament::Step() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ProgressCode::FeedingToFinda: // @@TODO this state will be reused for repeated cutting of filament ... probably there will be multiple attempts, not sure
|
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.Step()) {
|
||||||
if (feed.State() == FeedToFinda::Failed) {
|
if (feed.State() == FeedToFinda::Failed) {
|
||||||
// @@TODO
|
// @@TODO
|
||||||
|
|
@ -80,7 +73,7 @@ bool CutFilament::Step() {
|
||||||
case ProgressCode::EngagingIdler:
|
case ProgressCode::EngagingIdler:
|
||||||
if (mi::idler.Engaged()) {
|
if (mi::idler.Engaged()) {
|
||||||
state = ProgressCode::PushingFilament;
|
state = ProgressCode::PushingFilament;
|
||||||
mm::motion.PlanMove(cut_steps_pre, 0, 0, 1500, 0, 0); //@@TODO
|
mm::motion.PlanMove(cutStepsPre, 0, 0, 1500, 0, 0); //@@TODO
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ProgressCode::PushingFilament:
|
case ProgressCode::PushingFilament:
|
||||||
|
|
@ -109,4 +102,22 @@ bool CutFilament::Step() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProgressCode CutFilament::State() const {
|
||||||
|
switch (state) {
|
||||||
|
case ProgressCode::UnloadingFilament:
|
||||||
|
return unl.State(); // report sub-automaton states properly
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode CutFilament::Error() const {
|
||||||
|
switch (state) {
|
||||||
|
case ProgressCode::UnloadingFilament:
|
||||||
|
return unl.Error(); // report sub-automaton errors properly
|
||||||
|
default:
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace logic
|
} // namespace logic
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,13 @@ 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:
|
||||||
|
constexpr static const uint16_t cutStepsPre = 700;
|
||||||
|
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
|
UnloadFilament unl; ///< a high-level command/operation may be used as a building block of other operations as well
|
||||||
FeedToFinda feed;
|
FeedToFinda feed;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@ 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;
|
||||||
|
|
||||||
virtual ProgressCode State() const override;
|
ProgressCode State() const override;
|
||||||
|
|
||||||
virtual ErrorCode Error() const override;
|
ErrorCode Error() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr static const uint16_t ejectSteps = 500; //@@TODO
|
constexpr static const uint16_t ejectSteps = 500; //@@TODO
|
||||||
|
|
|
||||||
|
|
@ -11,32 +11,29 @@ namespace logic {
|
||||||
|
|
||||||
UnloadFilament unloadFilament;
|
UnloadFilament unloadFilament;
|
||||||
|
|
||||||
|
namespace mb = modules::buttons;
|
||||||
namespace mm = modules::motion;
|
namespace mm = modules::motion;
|
||||||
namespace mi = modules::idler;
|
namespace mi = modules::idler;
|
||||||
|
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::EngagingIdler;
|
state = ProgressCode::UnloadingToFinda;
|
||||||
error = ErrorCode::OK;
|
error = ErrorCode::OK;
|
||||||
modules::idler::idler.Engage(mg::globals.ActiveSlot());
|
unl.Reset(maxRetries);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnloadFilament::Step() {
|
bool UnloadFilament::Step() {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case ProgressCode::EngagingIdler: // state 1 engage idler
|
// state 1 engage idler - will be done by the Unload to FINDA state machine
|
||||||
if (mi::idler.Engaged()) { // if idler is in parked position un-park it get in contact with filament
|
|
||||||
state = ProgressCode::UnloadingToFinda;
|
|
||||||
unl.Reset();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case ProgressCode::UnloadingToFinda: // state 2 rotate pulley as long as the FINDA is on
|
case ProgressCode::UnloadingToFinda: // state 2 rotate pulley as long as the FINDA is on
|
||||||
if (unl.Step()) {
|
if (unl.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;
|
||||||
modules::leds::leds.SetMode(mg::globals.ActiveSlot(), modules::leds::red, modules::leds::blink0);
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::blink0);
|
||||||
} else {
|
} else {
|
||||||
state = ProgressCode::DisengagingIdler;
|
state = ProgressCode::DisengagingIdler;
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +43,8 @@ bool UnloadFilament::Step() {
|
||||||
return false;
|
return false;
|
||||||
case ProgressCode::DisengagingIdler:
|
case ProgressCode::DisengagingIdler:
|
||||||
if (!mi::idler.Engaged()) {
|
if (!mi::idler.Engaged()) {
|
||||||
state = ProgressCode::AvoidingGrind;
|
state = ProgressCode::AvoidingGrind; // @@TODO what was this originally? Why are we supposed to move the pulley when the idler is not engaged?
|
||||||
|
// may be the pulley was to move along with the idler?
|
||||||
// mm::motion.PlanMove(mm::Pulley, -100, 10); // @@TODO constants
|
// mm::motion.PlanMove(mm::Pulley, -100, 10); // @@TODO constants
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -71,19 +69,19 @@ bool UnloadFilament::Step() {
|
||||||
return false;
|
return false;
|
||||||
case ProgressCode::ERR1WaitingForUser: {
|
case ProgressCode::ERR1WaitingForUser: {
|
||||||
// waiting for user buttons and/or a command from the printer
|
// waiting for user buttons and/or a command from the printer
|
||||||
bool help = modules::buttons::buttons.ButtonPressed(modules::buttons::Left) /*|| command_help()*/;
|
bool help = mb::buttons.ButtonPressed(mb::Left) /*|| command_help()*/;
|
||||||
bool tryAgain = modules::buttons::buttons.ButtonPressed(modules::buttons::Middle) /*|| command_tryAgain()*/;
|
bool tryAgain = mb::buttons.ButtonPressed(mb::Middle) /*|| command_tryAgain()*/;
|
||||||
bool userResolved = modules::buttons::buttons.ButtonPressed(modules::buttons::Right) /*|| command_userResolved()*/;
|
bool userResolved = mb::buttons.ButtonPressed(mb::Right) /*|| command_userResolved()*/;
|
||||||
if (help) {
|
if (help) {
|
||||||
// try to manually unload just a tiny bit - help the filament with the pulley
|
// try to manually unload just a tiny bit - help the filament with the pulley
|
||||||
//@@TODO
|
//@@TODO
|
||||||
} else if (tryAgain) {
|
} else if (tryAgain) {
|
||||||
// try again the whole sequence
|
// try again the whole sequence
|
||||||
Reset(0); // @@TODO param
|
Reset(0);
|
||||||
} else if (userResolved) {
|
} else if (userResolved) {
|
||||||
// problem resolved - the user pulled the fillament by hand
|
// problem resolved - the user pulled the fillament by hand
|
||||||
modules::leds::leds.SetMode(mg::globals.ActiveSlot(), modules::leds::red, modules::leds::off);
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::red, ml::off);
|
||||||
modules::leds::leds.SetMode(mg::globals.ActiveSlot(), modules::leds::green, modules::leds::on);
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
|
||||||
// mm::motion.PlanMove(mm::Pulley, 450, 5000); // @@TODO constants
|
// mm::motion.PlanMove(mm::Pulley, 450, 5000); // @@TODO constants
|
||||||
state = ProgressCode::AvoidingGrind;
|
state = ProgressCode::AvoidingGrind;
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +90,10 @@ bool UnloadFilament::Step() {
|
||||||
case ProgressCode::OK:
|
case ProgressCode::OK:
|
||||||
mg::globals.SetFilamentLoaded(false); // filament unloaded
|
mg::globals.SetFilamentLoaded(false); // filament unloaded
|
||||||
return true; // successfully finished
|
return true; // successfully finished
|
||||||
|
default: // we got into an unhandled state, better report it
|
||||||
|
state = ProgressCode::ERRInternal;
|
||||||
|
error = ErrorCode::INTERNAL;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ namespace logic {
|
||||||
class UnloadFilament : public CommandBase {
|
class UnloadFilament : public CommandBase {
|
||||||
public:
|
public:
|
||||||
inline UnloadFilament()
|
inline UnloadFilament()
|
||||||
: CommandBase()
|
: CommandBase() {}
|
||||||
, unl(3) {}
|
|
||||||
|
|
||||||
/// Restart the automaton
|
/// Restart the automaton
|
||||||
void Reset(uint8_t param) override;
|
void Reset(uint8_t param) override;
|
||||||
|
|
@ -20,6 +19,7 @@ public:
|
||||||
bool Step() override;
|
bool Step() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
constexpr static const uint8_t maxRetries = 3;
|
||||||
UnloadToFinda unl;
|
UnloadToFinda unl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#include "unload_to_finda.h"
|
#include "unload_to_finda.h"
|
||||||
#include "../modules/buttons.h"
|
#include "../modules/buttons.h"
|
||||||
#include "../modules/finda.h"
|
#include "../modules/finda.h"
|
||||||
|
#include "../modules/globals.h"
|
||||||
|
#include "../modules/idler.h"
|
||||||
#include "../modules/leds.h"
|
#include "../modules/leds.h"
|
||||||
#include "../modules/motion.h"
|
#include "../modules/motion.h"
|
||||||
#include "../modules/permanent_storage.h"
|
#include "../modules/permanent_storage.h"
|
||||||
|
|
@ -8,34 +10,47 @@
|
||||||
namespace logic {
|
namespace logic {
|
||||||
|
|
||||||
namespace mm = modules::motion;
|
namespace mm = modules::motion;
|
||||||
|
namespace mg = modules::globals;
|
||||||
namespace mf = modules::finda;
|
namespace mf = modules::finda;
|
||||||
|
namespace mi = modules::idler;
|
||||||
|
namespace ml = modules::leds;
|
||||||
|
namespace mp = modules::permanent_storage;
|
||||||
|
|
||||||
void UnloadToFinda::Reset() {
|
void UnloadToFinda::Reset(uint8_t maxTries) {
|
||||||
|
this->maxTries = maxTries;
|
||||||
// check the inital state of FINDA and plan the moves
|
// check the inital state of FINDA and plan the moves
|
||||||
if (mf::finda.Pressed()) {
|
if (!mf::finda.Pressed()) {
|
||||||
state = OK; // FINDA is already off, we assume the fillament is not there, i.e. already unloaded
|
state = OK; // FINDA is already off, we assume the fillament is not there, i.e. already unloaded
|
||||||
} else {
|
} else {
|
||||||
// FINDA is sensing the filament, plan moves to unload it
|
// FINDA is sensing the filament, plan moves to unload it
|
||||||
int unloadSteps = /*BowdenLength::get() +*/ 1100; // @@TODO
|
state = EngagingIdler;
|
||||||
const int second_point = unloadSteps - 1300;
|
mi::idler.Engage(mg::globals.ActiveSlot());
|
||||||
// mm::motion.PlanMove(mm::Pulley, -1400, 6000); // @@TODO constants
|
|
||||||
// mm::motion.PlanMove(mm::Pulley, -1800 + 1400, 2500); // @@TODO constants 1800-1400 = 400
|
|
||||||
// mm::motion.PlanMove(mm::Pulley, -second_point + 1800, 550); // @@TODO constants
|
|
||||||
state = WaitingForFINDA;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnloadToFinda::Step() {
|
bool UnloadToFinda::Step() {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
case EngagingIdler:
|
||||||
|
if (mi::idler.Engaged()) {
|
||||||
|
state = WaitingForFINDA;
|
||||||
|
int unloadSteps = mp::BowdenLength::get() + 1100;
|
||||||
|
const int second_point = unloadSteps - 1300;
|
||||||
|
mm::motion.PlanMove(mm::Pulley, -1400, 6000); // @@TODO constants
|
||||||
|
mm::motion.PlanMove(mm::Pulley, -1800 + 1400, 2500); // @@TODO constants 1800-1400 = 400
|
||||||
|
mm::motion.PlanMove(mm::Pulley, -second_point + 1800, 550); // @@TODO constants
|
||||||
|
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::blink0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
case WaitingForFINDA:
|
case WaitingForFINDA:
|
||||||
if (modules::finda::finda.Pressed()) {
|
if (!mf::finda.Pressed()) {
|
||||||
// detected end of filament
|
// detected end of filament
|
||||||
state = OK;
|
state = OK;
|
||||||
|
mm::motion.AbortPlannedMoves(); // stop rotating the pulley
|
||||||
} else if (/*tmc2130_read_gstat() &&*/ mm::motion.QueueEmpty()) {
|
} else if (/*tmc2130_read_gstat() &&*/ mm::motion.QueueEmpty()) {
|
||||||
// we reached the end of move queue, but the FINDA didn't switch off
|
// we reached the end of move queue, but the FINDA didn't switch off
|
||||||
// two possible causes - grinded filament of malfunctioning FINDA
|
// two possible causes - grinded filament or malfunctioning FINDA
|
||||||
if (--maxTries) {
|
if (--maxTries) {
|
||||||
Reset(); // try again
|
Reset(maxTries); // try again
|
||||||
} else {
|
} else {
|
||||||
state = Failed;
|
state = Failed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,20 +21,25 @@ namespace logic {
|
||||||
///\enddot
|
///\enddot
|
||||||
struct UnloadToFinda {
|
struct UnloadToFinda {
|
||||||
enum {
|
enum {
|
||||||
|
EngagingIdler,
|
||||||
WaitingForFINDA,
|
WaitingForFINDA,
|
||||||
OK,
|
OK,
|
||||||
Failed
|
Failed
|
||||||
};
|
};
|
||||||
uint8_t state;
|
inline UnloadToFinda()
|
||||||
uint8_t maxTries;
|
: maxTries(3) {}
|
||||||
inline UnloadToFinda(uint8_t maxTries)
|
|
||||||
: maxTries(maxTries) { Reset(); }
|
|
||||||
|
|
||||||
/// Restart the automaton
|
/// Restart the automaton
|
||||||
void Reset();
|
void Reset(uint8_t maxTries);
|
||||||
|
|
||||||
/// @returns true if the state machine finished its job, false otherwise
|
/// @returns true if the state machine finished its job, false otherwise
|
||||||
bool Step();
|
bool Step();
|
||||||
|
|
||||||
|
inline uint8_t State() const { return state; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t maxTries;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace logic
|
} // namespace logic
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
namespace modules {
|
namespace modules {
|
||||||
namespace idler {
|
namespace idler {
|
||||||
|
|
||||||
|
// @@TODO PROGMEM
|
||||||
|
uint16_t const Idler::slotPositions[6] = { 1, 2, 3, 4, 5, 0 };
|
||||||
|
|
||||||
Idler idler;
|
Idler idler;
|
||||||
|
|
||||||
namespace mm = modules::motion;
|
namespace mm = modules::motion;
|
||||||
|
|
@ -22,7 +25,7 @@ bool Idler::Disengage() {
|
||||||
|
|
||||||
mm::motion.InitAxis(mm::Idler);
|
mm::motion.InitAxis(mm::Idler);
|
||||||
// plan move to idle position
|
// plan move to idle position
|
||||||
// mm::motion.PlanMove(0, idle_position, 0, 1000, 0, 0); // @@TODO
|
mm::motion.PlanMove(mm::Idler, slotPositions[5] - mm::motion.CurrentPos(mm::Idler), 1000); // @@TODO
|
||||||
state = Moving;
|
state = Moving;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +41,7 @@ bool Idler::Engage(uint8_t slot) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
mm::motion.InitAxis(mm::Idler);
|
mm::motion.InitAxis(mm::Idler);
|
||||||
// mm::motion.PlanMove(0, slotPositions[slot], 0, 1000, 0, 0); // @@TODO
|
mm::motion.PlanMove(mm::Idler, slotPositions[slot] - mm::motion.CurrentPos(mm::Idler), 1000); // @@TODO
|
||||||
state = Moving;
|
state = Moving;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +58,8 @@ bool Idler::Home() {
|
||||||
bool Idler::Step() {
|
bool Idler::Step() {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Moving:
|
case Moving:
|
||||||
if (mm::motion.QueueEmpty()) {
|
if (mm::motion.QueueEmpty()) { //@@TODO this will block until all axes made their movements,
|
||||||
|
// not sure if that is something we want
|
||||||
// move finished
|
// move finished
|
||||||
state = Ready;
|
state = Ready;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,16 @@ public:
|
||||||
/// this state is updated only when a planned move is successfully finished, so it is safe for higher-level
|
/// this state is updated only when a planned move is successfully finished, so it is safe for higher-level
|
||||||
/// state machines to use this call as a waiting condition for the desired state of the idler
|
/// state machines to use this call as a waiting condition for the desired state of the idler
|
||||||
inline bool Engaged() const { return currentlyEngaged; }
|
inline bool Engaged() const { return currentlyEngaged; }
|
||||||
|
|
||||||
|
/// @returns currently active slot
|
||||||
inline uint8_t Slot() const { return currentSlot; }
|
inline uint8_t Slot() const { return currentSlot; }
|
||||||
|
|
||||||
|
/// @returns predefined positions of individual slots
|
||||||
|
inline static uint16_t SlotPosition(uint8_t slot) { return slotPositions[slot]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr static const uint16_t slotPositions[5] = { 1, 2, 3, 4, 5 }; // @@TODO
|
/// slots 0-4 are the real ones, the 5th is the idle position
|
||||||
|
static const uint16_t slotPositions[6];
|
||||||
|
|
||||||
/// internal state of the automaton
|
/// internal state of the automaton
|
||||||
uint8_t state;
|
uint8_t state;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ void Motion::PlanMove(int16_t pulley, int16_t idler, int16_t selector, uint16_t
|
||||||
|
|
||||||
void Motion::PlanMove(Axis axis, int16_t delta, uint16_t feedrate) {}
|
void Motion::PlanMove(Axis axis, int16_t delta, uint16_t feedrate) {}
|
||||||
|
|
||||||
|
uint16_t Motion::CurrentPos(Axis axis) const { return 0; }
|
||||||
|
|
||||||
void Motion::Home(Axis axis, bool direction) {}
|
void Motion::Home(Axis axis, bool direction) {}
|
||||||
|
|
||||||
void Motion::SetMode(MotorMode mode) {}
|
void Motion::SetMode(MotorMode mode) {}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,10 @@ public:
|
||||||
/// @param feedrate maximum feedrate/speed after acceleration
|
/// @param feedrate maximum feedrate/speed after acceleration
|
||||||
void PlanMove(Axis axis, int16_t delta, uint16_t feedrate);
|
void PlanMove(Axis axis, int16_t delta, uint16_t feedrate);
|
||||||
|
|
||||||
|
/// @returns current position of an axis
|
||||||
|
/// @param axis axis affected
|
||||||
|
uint16_t CurrentPos(Axis axis) const;
|
||||||
|
|
||||||
/// Enqueue performing of homing of an axis
|
/// Enqueue performing of homing of an axis
|
||||||
/// @@TODO
|
/// @@TODO
|
||||||
void Home(Axis axis, bool direction);
|
void Home(Axis axis, bool direction);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
namespace modules {
|
namespace modules {
|
||||||
namespace selector {
|
namespace selector {
|
||||||
|
|
||||||
|
// @@TODO PROGMEM
|
||||||
|
const uint16_t Selector::slotPositions[6] = { 1, 2, 3, 4, 5, 6 }; // @@TODO
|
||||||
|
|
||||||
Selector selector;
|
Selector selector;
|
||||||
|
|
||||||
namespace mm = modules::motion;
|
namespace mm = modules::motion;
|
||||||
|
|
@ -21,7 +24,7 @@ bool Selector::MoveToSlot(uint8_t slot) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
mm::motion.InitAxis(mm::Selector);
|
mm::motion.InitAxis(mm::Selector);
|
||||||
// mm::motion.PlanMove(1, slotPositions[slot], 0, 1000, 0, 0); // @@TODO
|
mm::motion.PlanMove(mm::Selector, slotPositions[slot] - mm::motion.CurrentPos(mm::Selector), 1000); // @@TODO
|
||||||
state = Moving;
|
state = Moving;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,12 @@ public:
|
||||||
/// state machines to use this call as a waiting condition for the desired state of the selector
|
/// state machines to use this call as a waiting condition for the desired state of the selector
|
||||||
inline uint8_t Slot() const { return currentSlot; }
|
inline uint8_t Slot() const { return currentSlot; }
|
||||||
|
|
||||||
|
/// @returns predefined positions of individual slots
|
||||||
|
inline static uint16_t SlotPosition(uint8_t slot) { return slotPositions[slot]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr static const uint16_t slotPositions[5] = { 1, 2, 3, 4, 5 }; // @@TODO
|
/// slots 0-4 are the real ones, the 5th is the farthest parking positions
|
||||||
|
static const uint16_t slotPositions[6];
|
||||||
|
|
||||||
/// internal state of the automaton
|
/// internal state of the automaton
|
||||||
uint8_t state;
|
uint8_t state;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ add_subdirectory(feed_to_finda)
|
||||||
|
|
||||||
# add_subdirectory(feed_to_bondtech)
|
# add_subdirectory(feed_to_bondtech)
|
||||||
|
|
||||||
# add_subdirectory(unload_to_finda)
|
add_subdirectory(unload_to_finda)
|
||||||
|
|
||||||
add_subdirectory(eject_filament)
|
add_subdirectory(eject_filament)
|
||||||
|
|
||||||
|
|
@ -14,4 +14,4 @@ add_subdirectory(eject_filament)
|
||||||
|
|
||||||
# add_subdirectory(cut_filament)
|
# add_subdirectory(cut_filament)
|
||||||
|
|
||||||
# add_subdirectory(unload_filament)
|
add_subdirectory(unload_filament)
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,13 @@
|
||||||
|
|
||||||
using Catch::Matchers::Equals;
|
using Catch::Matchers::Equals;
|
||||||
|
|
||||||
template <typename COND>
|
namespace mm = modules::motion;
|
||||||
bool WhileCondition(COND cond, uint32_t maxLoops = 5000) {
|
namespace mf = modules::finda;
|
||||||
while (cond() && --maxLoops) {
|
namespace mi = modules::idler;
|
||||||
main_loop();
|
namespace ml = modules::leds;
|
||||||
}
|
namespace mb = modules::buttons;
|
||||||
return maxLoops > 0;
|
namespace mg = modules::globals;
|
||||||
}
|
namespace ms = modules::selector;
|
||||||
|
|
||||||
TEST_CASE("cut_filament::cut0", "[cut_filament]") {
|
TEST_CASE("cut_filament::cut0", "[cut_filament]") {
|
||||||
using namespace logic;
|
using namespace logic;
|
||||||
|
|
@ -41,36 +41,39 @@ TEST_CASE("cut_filament::cut0", "[cut_filament]") {
|
||||||
|
|
||||||
// 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 1
|
||||||
// check if the idler and selector have the right command
|
// check if the idler and selector have the right command
|
||||||
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants
|
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == mi::Idler::SlotPosition(0));
|
||||||
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == 0); // @@TODO constants
|
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.State() == ProgressCode::SelectingFilamentSlot; }, 5000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == 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
|
// idler and selector reached their target positions and the CF automaton will start feeding to FINDA as the next step
|
||||||
REQUIRE(cf.State() == 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);
|
hal::adc::ReinitADC(1, hal::adc::TADCData({ 0, 0, 0, 0, 600, 700, 800, 900 }), 10);
|
||||||
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::FeedingToFinda; }, 50000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::FeedingToFinda; }, 5000));
|
||||||
|
|
||||||
// filament fed into FINDA, cutting...
|
// filament fed into FINDA, cutting...
|
||||||
REQUIRE(cf.State() == ProgressCode::PreparingBlade);
|
REQUIRE(cf.TopLevelState() == ProgressCode::PreparingBlade);
|
||||||
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::PreparingBlade; }, 5000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::PreparingBlade; }, 5000));
|
||||||
|
|
||||||
REQUIRE(cf.State() == ProgressCode::EngagingIdler);
|
REQUIRE(cf.TopLevelState() == ProgressCode::EngagingIdler);
|
||||||
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::EngagingIdler; }, 5000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::EngagingIdler; }, 5000));
|
||||||
|
|
||||||
// the idler should be at the active slot @@TODO
|
// the idler should be at the active slot @@TODO
|
||||||
REQUIRE(cf.State() == ProgressCode::PushingFilament);
|
REQUIRE(cf.TopLevelState() == ProgressCode::PushingFilament);
|
||||||
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::PushingFilament; }, 5000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::PushingFilament; }, 5000));
|
||||||
|
|
||||||
// filament pushed - performing cut
|
// filament pushed - performing cut
|
||||||
REQUIRE(cf.State() == ProgressCode::PerformingCut);
|
REQUIRE(cf.TopLevelState() == ProgressCode::PerformingCut);
|
||||||
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::PerformingCut; }, 5000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::PerformingCut; }, 5000));
|
||||||
|
|
||||||
// returning selector
|
// returning selector
|
||||||
REQUIRE(cf.State() == ProgressCode::ReturningSelector);
|
REQUIRE(cf.TopLevelState() == ProgressCode::ReturningSelector);
|
||||||
REQUIRE(WhileCondition([&]() { return cf.State() == ProgressCode::ReturningSelector; }, 5000));
|
REQUIRE(WhileCondition([&]() { return cf.TopLevelState() == ProgressCode::ReturningSelector; }, 5000));
|
||||||
|
|
||||||
// the next states are still @@TODO
|
// the next states are still @@TODO
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,13 @@
|
||||||
|
|
||||||
using Catch::Matchers::Equals;
|
using Catch::Matchers::Equals;
|
||||||
|
|
||||||
template <typename COND>
|
namespace mm = modules::motion;
|
||||||
bool WhileCondition(COND cond, uint32_t maxLoops = 5000) {
|
namespace mf = modules::finda;
|
||||||
while (cond() && --maxLoops) {
|
namespace mi = modules::idler;
|
||||||
main_loop();
|
namespace ml = modules::leds;
|
||||||
}
|
namespace mb = modules::buttons;
|
||||||
return maxLoops > 0;
|
namespace mg = modules::globals;
|
||||||
}
|
namespace ms = modules::selector;
|
||||||
|
|
||||||
TEST_CASE("eject_filament::eject0", "[eject_filament]") {
|
TEST_CASE("eject_filament::eject0", "[eject_filament]") {
|
||||||
using namespace logic;
|
using namespace logic;
|
||||||
|
|
@ -41,36 +41,36 @@ TEST_CASE("eject_filament::eject0", "[eject_filament]") {
|
||||||
|
|
||||||
// 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 1
|
||||||
// check if the idler and selector have the right command
|
// check if the idler and selector have the right command
|
||||||
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == 0); // @@TODO constants
|
CHECK(modules::motion::axes[modules::motion::Idler].targetPos == mi::Idler::SlotPosition(0));
|
||||||
CHECK(modules::motion::axes[modules::motion::Selector].targetPos == 0); // @@TODO constants
|
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.State() == ProgressCode::SelectingFilamentSlot; }, 5000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == 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.State() == 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.State() == ProgressCode::FeedingToFinda; }, 50000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::FeedingToFinda; }, 50000));
|
||||||
|
|
||||||
// filament fed into FINDA, cutting...
|
// filament fed into FINDA, cutting...
|
||||||
REQUIRE(ef.State() == ProgressCode::PreparingBlade);
|
REQUIRE(ef.TopLevelState() == ProgressCode::PreparingBlade);
|
||||||
REQUIRE(WhileCondition([&]() { return ef.State() == ProgressCode::PreparingBlade; }, 5000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::PreparingBlade; }, 5000));
|
||||||
|
|
||||||
REQUIRE(ef.State() == ProgressCode::EngagingIdler);
|
REQUIRE(ef.TopLevelState() == ProgressCode::EngagingIdler);
|
||||||
REQUIRE(WhileCondition([&]() { return ef.State() == ProgressCode::EngagingIdler; }, 5000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::EngagingIdler; }, 5000));
|
||||||
|
|
||||||
// the idler should be at the active slot @@TODO
|
// the idler should be at the active slot @@TODO
|
||||||
REQUIRE(ef.State() == ProgressCode::PushingFilament);
|
REQUIRE(ef.TopLevelState() == ProgressCode::PushingFilament);
|
||||||
REQUIRE(WhileCondition([&]() { return ef.State() == ProgressCode::PushingFilament; }, 5000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::PushingFilament; }, 5000));
|
||||||
|
|
||||||
// filament pushed - performing cut
|
// filament pushed - performing cut
|
||||||
REQUIRE(ef.State() == ProgressCode::PerformingCut);
|
REQUIRE(ef.TopLevelState() == ProgressCode::PerformingCut);
|
||||||
REQUIRE(WhileCondition([&]() { return ef.State() == ProgressCode::PerformingCut; }, 5000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::PerformingCut; }, 5000));
|
||||||
|
|
||||||
// returning selector
|
// returning selector
|
||||||
REQUIRE(ef.State() == ProgressCode::ReturningSelector);
|
REQUIRE(ef.TopLevelState() == ProgressCode::ReturningSelector);
|
||||||
REQUIRE(WhileCondition([&]() { return ef.State() == ProgressCode::ReturningSelector; }, 5000));
|
REQUIRE(WhileCondition([&]() { return ef.TopLevelState() == ProgressCode::ReturningSelector; }, 5000));
|
||||||
|
|
||||||
// the next states are still @@TODO
|
// the next states are still @@TODO
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
|
||||||
|
|
||||||
// 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 1
|
||||||
// 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 == 0); // @@TODO constants
|
CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0));
|
||||||
CHECK(mm::axes[mm::Selector].targetPos == 0); // @@TODO constants
|
CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0));
|
||||||
CHECK(mm::axes[mm::Idler].enabled == true); // @@TODO constants
|
CHECK(mm::axes[mm::Idler].enabled == true);
|
||||||
|
|
||||||
// engaging idler
|
// engaging idler
|
||||||
REQUIRE(WhileCondition(
|
REQUIRE(WhileCondition(
|
||||||
|
|
@ -63,6 +63,9 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
|
||||||
[&]() { return !mi::idler.Engaged(); },
|
[&]() { return !mi::idler.Engaged(); },
|
||||||
5000));
|
5000));
|
||||||
|
|
||||||
|
CHECK(mm::axes[mm::Idler].pos == mi::Idler::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() == FeedToFinda::PushingFilament);
|
REQUIRE(ff.State() == FeedToFinda::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)
|
||||||
|
|
@ -92,6 +95,9 @@ TEST_CASE("feed_to_finda::feed_phase_unlimited", "[feed_to_finda]") {
|
||||||
[&]() { return mi::idler.Engaged(); },
|
[&]() { return mi::idler.Engaged(); },
|
||||||
5000));
|
5000));
|
||||||
|
|
||||||
|
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5)); // @@TODO constants
|
||||||
|
CHECK(mm::axes[mm::Selector].pos == ms::Selector::SlotPosition(0));
|
||||||
|
|
||||||
// state machine finished ok, the green LED should be on
|
// state machine finished ok, the green LED should be on
|
||||||
REQUIRE(ff.State() == FeedToFinda::OK);
|
REQUIRE(ff.State() == FeedToFinda::OK);
|
||||||
REQUIRE(ml::leds.LedOn(mg::globals.ActiveSlot(), ml::Color::green));
|
REQUIRE(ml::leds.LedOn(mg::globals.ActiveSlot(), ml::Color::green));
|
||||||
|
|
@ -114,8 +120,8 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
|
||||||
|
|
||||||
// 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 1
|
||||||
// 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 == 0); // @@TODO constants
|
CHECK(mm::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0));
|
||||||
CHECK(mm::axes[mm::Selector].targetPos == 0); // @@TODO constants
|
CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0));
|
||||||
|
|
||||||
// engaging idler
|
// engaging idler
|
||||||
REQUIRE(WhileCondition(
|
REQUIRE(WhileCondition(
|
||||||
|
|
@ -123,6 +129,9 @@ TEST_CASE("feed_to_finda::FINDA_failed", "[feed_to_finda]") {
|
||||||
[&]() { return !mi::idler.Engaged(); },
|
[&]() { return !mi::idler.Engaged(); },
|
||||||
5000));
|
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
|
// idler engaged, we'll start pushing filament
|
||||||
REQUIRE(ff.State() == FeedToFinda::PushingFilament);
|
REQUIRE(ff.State() == FeedToFinda::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)
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,11 @@ extern void main_loop();
|
||||||
extern void ForceReinitAllAutomata();
|
extern void ForceReinitAllAutomata();
|
||||||
|
|
||||||
extern logic::CommandBase *currentCommand;
|
extern logic::CommandBase *currentCommand;
|
||||||
|
|
||||||
|
template <typename COND>
|
||||||
|
bool WhileCondition(COND cond, uint32_t maxLoops = 5000) {
|
||||||
|
while (cond() && --maxLoops) {
|
||||||
|
main_loop();
|
||||||
|
}
|
||||||
|
return maxLoops > 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ namespace motion {
|
||||||
|
|
||||||
Motion motion;
|
Motion motion;
|
||||||
AxisSim axes[3] = {
|
AxisSim axes[3] = {
|
||||||
{ 0, 0, false, false, false },
|
{ 0, 0, false, false, false }, // pulley
|
||||||
{ 0, 0, false, false, false },
|
{ 1, 1, false, false, false }, // selector //@@TODO proper selector positions once defined
|
||||||
{ 0, 0, false, false, false },
|
{ 0, 0, false, false, false }, // idler
|
||||||
};
|
};
|
||||||
|
|
||||||
void Motion::InitAxis(Axis axis) {
|
void Motion::InitAxis(Axis axis) {
|
||||||
|
|
@ -38,6 +38,10 @@ void Motion::PlanMove(Axis axis, int16_t delta, uint16_t feedrate) {
|
||||||
axes[axis].targetPos = axes[axis].pos + delta;
|
axes[axis].targetPos = axes[axis].pos + delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t Motion::CurrentPos(Axis axis) const {
|
||||||
|
return axes[axis].pos;
|
||||||
|
}
|
||||||
|
|
||||||
void Motion::Home(Axis axis, bool direction) {
|
void Motion::Home(Axis axis, bool direction) {
|
||||||
axes[Pulley].homed = true;
|
axes[Pulley].homed = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# define the test executable
|
||||||
|
add_executable(
|
||||||
|
unload_filament_tests
|
||||||
|
../../../../src/logic/feed_to_finda.cpp
|
||||||
|
../../../../src/logic/unload_filament.cpp
|
||||||
|
../../../../src/logic/unload_to_finda.cpp
|
||||||
|
../../../../src/modules/buttons.cpp
|
||||||
|
../../../../src/modules/debouncer.cpp
|
||||||
|
../../../../src/modules/finda.cpp
|
||||||
|
../../../../src/modules/fsensor.cpp
|
||||||
|
../../../../src/modules/globals.cpp
|
||||||
|
../../../../src/modules/idler.cpp
|
||||||
|
../../../../src/modules/leds.cpp
|
||||||
|
../../../../src/modules/permanent_storage.cpp
|
||||||
|
../../../../src/modules/selector.cpp
|
||||||
|
../../modules/stubs/stub_adc.cpp
|
||||||
|
../../modules/stubs/stub_eeprom.cpp
|
||||||
|
../../modules/stubs/stub_shr16.cpp
|
||||||
|
../../modules/stubs/stub_timebase.cpp
|
||||||
|
../stubs/main_loop_stub.cpp
|
||||||
|
../stubs/stub_motion.cpp
|
||||||
|
test_unload_filament.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# define required search paths
|
||||||
|
target_include_directories(
|
||||||
|
unload_filament_tests PUBLIC ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/src/hal
|
||||||
|
${CMAKE_SOURCE_DIR}/src/logic
|
||||||
|
)
|
||||||
|
|
||||||
|
# tell build system about the test case
|
||||||
|
add_catch_test(unload_filament_tests)
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
|
#include "../../../../src/modules/buttons.h"
|
||||||
|
#include "../../../../src/modules/finda.h"
|
||||||
|
#include "../../../../src/modules/fsensor.h"
|
||||||
|
#include "../../../../src/modules/globals.h"
|
||||||
|
#include "../../../../src/modules/idler.h"
|
||||||
|
#include "../../../../src/modules/leds.h"
|
||||||
|
#include "../../../../src/modules/motion.h"
|
||||||
|
#include "../../../../src/modules/permanent_storage.h"
|
||||||
|
#include "../../../../src/modules/selector.h"
|
||||||
|
|
||||||
|
#include "../../../../src/logic/unload_filament.h"
|
||||||
|
|
||||||
|
#include "../../modules/stubs/stub_adc.h"
|
||||||
|
|
||||||
|
#include "../stubs/main_loop_stub.h"
|
||||||
|
#include "../stubs/stub_motion.h"
|
||||||
|
|
||||||
|
using Catch::Matchers::Equals;
|
||||||
|
|
||||||
|
namespace mm = modules::motion;
|
||||||
|
namespace mf = modules::finda;
|
||||||
|
namespace mi = modules::idler;
|
||||||
|
namespace ml = modules::leds;
|
||||||
|
namespace mb = modules::buttons;
|
||||||
|
namespace mg = modules::globals;
|
||||||
|
namespace ms = modules::selector;
|
||||||
|
|
||||||
|
TEST_CASE("unload_filament::unload0", "[unload_filament]") {
|
||||||
|
using namespace logic;
|
||||||
|
|
||||||
|
ForceReinitAllAutomata();
|
||||||
|
|
||||||
|
UnloadFilament uf;
|
||||||
|
// restart the automaton
|
||||||
|
currentCommand = &uf;
|
||||||
|
uf.Reset(0);
|
||||||
|
|
||||||
|
main_loop();
|
||||||
|
|
||||||
|
REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::UnloadingToFinda; }, 5000));
|
||||||
|
|
||||||
|
REQUIRE(uf.TopLevelState() == ProgressCode::DisengagingIdler);
|
||||||
|
REQUIRE(WhileCondition([&]() { return uf.TopLevelState() == ProgressCode::DisengagingIdler; }, 5000));
|
||||||
|
|
||||||
|
CHECK(mm::axes[mm::Idler].pos == mi::Idler::SlotPosition(5));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# define the test executable
|
||||||
|
add_executable(
|
||||||
|
unload_to_finda_tests
|
||||||
|
../../../../src/logic/unload_to_finda.cpp
|
||||||
|
../../../../src/modules/buttons.cpp
|
||||||
|
../../../../src/modules/debouncer.cpp
|
||||||
|
../../../../src/modules/finda.cpp
|
||||||
|
../../../../src/modules/fsensor.cpp
|
||||||
|
../../../../src/modules/globals.cpp
|
||||||
|
../../../../src/modules/idler.cpp
|
||||||
|
../../../../src/modules/leds.cpp
|
||||||
|
../../../../src/modules/permanent_storage.cpp
|
||||||
|
../../../../src/modules/selector.cpp
|
||||||
|
../../modules/stubs/stub_adc.cpp
|
||||||
|
../../modules/stubs/stub_eeprom.cpp
|
||||||
|
../../modules/stubs/stub_shr16.cpp
|
||||||
|
../../modules/stubs/stub_timebase.cpp
|
||||||
|
../stubs/main_loop_stub.cpp
|
||||||
|
../stubs/stub_motion.cpp
|
||||||
|
test_unload_to_finda.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# define required search paths
|
||||||
|
target_include_directories(
|
||||||
|
unload_to_finda_tests PUBLIC ${CMAKE_SOURCE_DIR}/src/modules ${CMAKE_SOURCE_DIR}/src/hal
|
||||||
|
${CMAKE_SOURCE_DIR}/src/logic
|
||||||
|
)
|
||||||
|
|
||||||
|
# tell build system about the test case
|
||||||
|
add_catch_test(unload_to_finda_tests)
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
|
#include "../../../../src/modules/buttons.h"
|
||||||
|
#include "../../../../src/modules/finda.h"
|
||||||
|
#include "../../../../src/modules/fsensor.h"
|
||||||
|
#include "../../../../src/modules/globals.h"
|
||||||
|
#include "../../../../src/modules/idler.h"
|
||||||
|
#include "../../../../src/modules/leds.h"
|
||||||
|
#include "../../../../src/modules/motion.h"
|
||||||
|
#include "../../../../src/modules/permanent_storage.h"
|
||||||
|
#include "../../../../src/modules/selector.h"
|
||||||
|
|
||||||
|
#include "../../../../src/logic/unload_to_finda.h"
|
||||||
|
|
||||||
|
#include "../../modules/stubs/stub_adc.h"
|
||||||
|
|
||||||
|
#include "../stubs/main_loop_stub.h"
|
||||||
|
#include "../stubs/stub_motion.h"
|
||||||
|
|
||||||
|
using Catch::Matchers::Equals;
|
||||||
|
|
||||||
|
namespace mm = modules::motion;
|
||||||
|
namespace mf = modules::finda;
|
||||||
|
namespace mi = modules::idler;
|
||||||
|
namespace ml = modules::leds;
|
||||||
|
namespace mb = modules::buttons;
|
||||||
|
namespace mg = modules::globals;
|
||||||
|
namespace ms = modules::selector;
|
||||||
|
|
||||||
|
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]") {
|
||||||
|
using namespace logic;
|
||||||
|
|
||||||
|
ForceReinitAllAutomata();
|
||||||
|
|
||||||
|
// we need finda ON
|
||||||
|
hal::adc::ReinitADC(1, hal::adc::TADCData({ 1023 }), 1);
|
||||||
|
|
||||||
|
UnloadToFinda ff;
|
||||||
|
|
||||||
|
// wait for FINDA to debounce
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
[&]() { return !mf::finda.Pressed(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// restart the automaton - just 1 attempt
|
||||||
|
ff.Reset(1);
|
||||||
|
|
||||||
|
REQUIRE(ff.State() == 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::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0));
|
||||||
|
CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0));
|
||||||
|
CHECK(mm::axes[mm::Idler].enabled == true);
|
||||||
|
|
||||||
|
// engaging idler
|
||||||
|
REQUIRE(WhileConditionFF(
|
||||||
|
ff,
|
||||||
|
[&]() { return !mi::idler.Engaged(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// now pulling the filament until finda triggers
|
||||||
|
REQUIRE(ff.State() == UnloadToFinda::WaitingForFINDA);
|
||||||
|
hal::adc::ReinitADC(1, hal::adc::TADCData({ 1023, 900, 800, 500, 0 }), 10);
|
||||||
|
REQUIRE(WhileConditionFF(
|
||||||
|
ff,
|
||||||
|
[&]() { return mf::finda.Pressed(); },
|
||||||
|
50000));
|
||||||
|
|
||||||
|
REQUIRE(ff.State() == UnloadToFinda::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("unload_to_finda::no_sense_FINDA_upon_start", "[unload_to_finda]") {
|
||||||
|
using namespace logic;
|
||||||
|
|
||||||
|
ForceReinitAllAutomata(); // that implies FINDA OFF which should really not happen for an unload call
|
||||||
|
|
||||||
|
UnloadToFinda ff;
|
||||||
|
|
||||||
|
// restart the automaton - just 1 attempt
|
||||||
|
ff.Reset(1);
|
||||||
|
|
||||||
|
// the state machine should accept the unpressed FINDA as no-fillament-loaded
|
||||||
|
// thus should immediately end in the OK state
|
||||||
|
REQUIRE(ff.State() == UnloadToFinda::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]") {
|
||||||
|
using namespace logic;
|
||||||
|
|
||||||
|
ForceReinitAllAutomata();
|
||||||
|
|
||||||
|
// we need finda ON
|
||||||
|
hal::adc::ReinitADC(1, hal::adc::TADCData({ 1023 }), 1);
|
||||||
|
|
||||||
|
UnloadToFinda ff;
|
||||||
|
|
||||||
|
// wait for FINDA to debounce
|
||||||
|
REQUIRE(WhileCondition(
|
||||||
|
[&]() { return !mf::finda.Pressed(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// restart the automaton - just 1 attempt
|
||||||
|
ff.Reset(1);
|
||||||
|
|
||||||
|
REQUIRE(ff.State() == 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::axes[mm::Idler].targetPos == mi::Idler::SlotPosition(0));
|
||||||
|
CHECK(mm::axes[mm::Selector].targetPos == ms::Selector::SlotPosition(0));
|
||||||
|
CHECK(mm::axes[mm::Idler].enabled == true);
|
||||||
|
|
||||||
|
// engaging idler
|
||||||
|
REQUIRE(WhileConditionFF(
|
||||||
|
ff,
|
||||||
|
[&]() { return !mi::idler.Engaged(); },
|
||||||
|
5000));
|
||||||
|
|
||||||
|
// now pulling the filament until finda triggers
|
||||||
|
REQUIRE(ff.State() == UnloadToFinda::WaitingForFINDA);
|
||||||
|
|
||||||
|
// no changes to FINDA during unload - we'll pretend it never triggers
|
||||||
|
REQUIRE(!WhileConditionFF(
|
||||||
|
ff,
|
||||||
|
[&]() { return mf::finda.Pressed(); },
|
||||||
|
50000));
|
||||||
|
|
||||||
|
REQUIRE(ff.State() == UnloadToFinda::Failed);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue