Extract Unload filament into a separate file
+ cleanup the object structure + add no_command as the starting "operation" to simplify the rest of the command handling at runtimepull/21/head
parent
271a2dd7df
commit
5b4eb0cee3
|
|
@ -195,7 +195,10 @@ target_sources(
|
||||||
src/modules/finda.cpp
|
src/modules/finda.cpp
|
||||||
src/modules/leds.cpp
|
src/modules/leds.cpp
|
||||||
src/modules/motion.cpp
|
src/modules/motion.cpp
|
||||||
src/logic/mm_control.cpp
|
src/logic/command_base.cpp
|
||||||
|
src/logic/no_command.cpp
|
||||||
|
src/logic/unload_filament.cpp
|
||||||
|
src/logic/unload_to_finda.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(
|
set_property(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "command_base.h"
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "error_codes.h"
|
||||||
|
#include "progress_codes.h"
|
||||||
|
|
||||||
|
/// Base class defining common API for high-level operations/commands/state machines
|
||||||
|
///
|
||||||
|
/// Which state machines are high-level? Those which are being initiated either by a command over the serial line or from a button
|
||||||
|
/// - they report their progress to the printer
|
||||||
|
/// - they can be composed of other sub automatons
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
/// Tasks derived from this base class are the top-level operations inhibited by the printer.
|
||||||
|
/// These tasks report their progress and only one of these tasks is allowed to run at once.
|
||||||
|
class CommandBase {
|
||||||
|
public:
|
||||||
|
inline CommandBase()
|
||||||
|
: state(ProgressCode::OK)
|
||||||
|
, error(ErrorCode::OK) {}
|
||||||
|
|
||||||
|
// Normally, a base class should (must) have a virtual destructor to enable correct deallocation of superstructures.
|
||||||
|
// However, in our case we don't want ANY destruction of these objects and moreover - adding a destructor like this
|
||||||
|
// makes the linker complain about missing operator delete(), which is really not something we want/need in our case.
|
||||||
|
// Without the destructor, the linker is "happy" ;)
|
||||||
|
// virtual ~CommandBase() = default;
|
||||||
|
|
||||||
|
/// resets the automaton
|
||||||
|
virtual void Reset() = 0;
|
||||||
|
|
||||||
|
/// steps the state machine
|
||||||
|
/// @returns true if the automaton finished its work
|
||||||
|
virtual bool Step() = 0;
|
||||||
|
|
||||||
|
/// @returns progress of operation - each automaton consists of several internal states
|
||||||
|
/// 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
|
||||||
|
virtual ProgressCode State() const { return state; }
|
||||||
|
|
||||||
|
/// @returns status of the operation - e.g. RUNNING, OK, or an error code if the operation failed
|
||||||
|
/// Please see @ErrorCode for more details
|
||||||
|
virtual ErrorCode Error() const { return error; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ProgressCode state;
|
||||||
|
ErrorCode error;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// A complete set of error codes which may be a result of a high-level command/operation
|
||||||
|
/// This header file shall be included in the printer's firmware as well as a reference,
|
||||||
|
/// therefore the error codes have been extracted to one place
|
||||||
|
|
||||||
|
enum class ErrorCode : int_fast8_t {
|
||||||
|
RUNNING = 0, ///< the operation is still running
|
||||||
|
OK, ///< the operation finished OK
|
||||||
|
|
||||||
|
/// Unload Filament related error codes
|
||||||
|
UNLOAD_FINDA_DIDNT_TRIGGER = -1, ///< FINDA didn't trigger while unloading filament - either there is something blocking the metal ball or a cable is broken/disconnected
|
||||||
|
UNLOAD_ERROR2 = -2,
|
||||||
|
};
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
#include "mm_control.h"
|
|
||||||
#include "../modules/motion.h"
|
|
||||||
#include "../modules/leds.h"
|
|
||||||
#include "../modules/buttons.h"
|
|
||||||
#include "../modules/finda.h"
|
|
||||||
#include "../modules/permanent_storage.h"
|
|
||||||
|
|
||||||
namespace logic {
|
|
||||||
|
|
||||||
// "small" state machines will serve as building blocks for high-level commands/operations
|
|
||||||
// - engage/disengage idler
|
|
||||||
// - rotate pulley to some direction as long as the FINDA is on/off
|
|
||||||
// - rotate some axis to some fixed direction
|
|
||||||
// - load/unload to finda
|
|
||||||
//
|
|
||||||
|
|
||||||
// motion planning
|
|
||||||
// - we need some kind of planner buffer, especially because of accelerations
|
|
||||||
// because we may need to match the ramps between moves seamlessly - just like on a printer
|
|
||||||
|
|
||||||
/// A "small" automaton example - Try to unload filament to FINDA and if it fails try to recover several times.
|
|
||||||
/// \dot
|
|
||||||
/// digraph example {
|
|
||||||
/// node [shape=record, fontname=Helvetica, fontsize=10];
|
|
||||||
/// b [ label="class B" URL="\ref B"];
|
|
||||||
/// c [ label="class C" URL="\ref C"];
|
|
||||||
/// b -> c [ arrowhead="open", style="dashed" ];
|
|
||||||
///}
|
|
||||||
///\enddot
|
|
||||||
struct UnloadToFinda {
|
|
||||||
enum {
|
|
||||||
WaitingForFINDA,
|
|
||||||
OK,
|
|
||||||
Failed
|
|
||||||
};
|
|
||||||
uint8_t state;
|
|
||||||
uint8_t maxTries;
|
|
||||||
inline UnloadToFinda(uint8_t maxTries)
|
|
||||||
: maxTries(maxTries) { Reset(); }
|
|
||||||
|
|
||||||
/// Restart the automaton
|
|
||||||
inline void Reset() {
|
|
||||||
namespace mm = modules::motion;
|
|
||||||
namespace mf = modules::finda;
|
|
||||||
// check the inital state of FINDA and plan the moves
|
|
||||||
if (mf::finda.Pressed()) {
|
|
||||||
state = OK; // FINDA is already off, we assume the fillament is not there, i.e. already unloaded
|
|
||||||
} else {
|
|
||||||
// FINDA is sensing the filament, plan moves to unload it
|
|
||||||
int unloadSteps = /*BowdenLength::get() +*/ 1100; // @@TODO
|
|
||||||
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
|
|
||||||
state = WaitingForFINDA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @returns true if the state machine finished its job, false otherwise
|
|
||||||
bool Step() {
|
|
||||||
namespace mm = modules::motion;
|
|
||||||
namespace mf = modules::finda;
|
|
||||||
switch (state) {
|
|
||||||
case WaitingForFINDA:
|
|
||||||
if (modules::finda::finda.Pressed()) {
|
|
||||||
// detected end of filament
|
|
||||||
state = OK;
|
|
||||||
} else if (/*tmc2130_read_gstat() &&*/ mm::motion.QueueEmpty()) {
|
|
||||||
// we reached the end of move queue, but the FINDA didn't switch off
|
|
||||||
// two possible causes - grinded filament of malfunctioning FINDA
|
|
||||||
if (--maxTries) {
|
|
||||||
Reset(); // try again
|
|
||||||
} else {
|
|
||||||
state = Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case OK:
|
|
||||||
case Failed:
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A high-level command state machine
|
|
||||||
/// Handles the complex logic of unloading filament
|
|
||||||
class UnloadFilament : public TaskBase {
|
|
||||||
enum State {
|
|
||||||
EngagingIdler,
|
|
||||||
UnloadingToFinda,
|
|
||||||
DisengagingIdler,
|
|
||||||
AvoidingGrind,
|
|
||||||
Finishing,
|
|
||||||
OK,
|
|
||||||
ERR1DisengagingIdler,
|
|
||||||
ERR1WaitingForUser
|
|
||||||
};
|
|
||||||
|
|
||||||
UnloadToFinda unl;
|
|
||||||
|
|
||||||
inline UnloadFilament()
|
|
||||||
: TaskBase()
|
|
||||||
, unl(3) { Reset(); }
|
|
||||||
|
|
||||||
/// Restart the automaton
|
|
||||||
void Reset() override {
|
|
||||||
namespace mm = modules::motion;
|
|
||||||
// unloads filament from extruder - filament is above Bondtech gears
|
|
||||||
mm::motion.InitAxis(mm::Pulley);
|
|
||||||
state = EngagingIdler;
|
|
||||||
mm::motion.Idler(mm::Engage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @returns true if the state machine finished its job, false otherwise
|
|
||||||
bool Step() override {
|
|
||||||
namespace mm = modules::motion;
|
|
||||||
switch (state) {
|
|
||||||
case EngagingIdler: // state 1 engage idler
|
|
||||||
if (mm::motion.IdlerEngaged()) { // if idler is in parked position un-park it get in contact with filament
|
|
||||||
state = UnloadingToFinda;
|
|
||||||
unl.Reset();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case UnloadingToFinda: // state 2 rotate pulley as long as the FINDA is on
|
|
||||||
if (unl.Step()) {
|
|
||||||
if (unl.state == UnloadToFinda::Failed) {
|
|
||||||
// couldn't unload to FINDA, report error and wait for user to resolve it
|
|
||||||
state = ERR1DisengagingIdler;
|
|
||||||
// modules::leds::leds.SetMode(active_extruder, modules::leds::red, modules::leds::blink0);
|
|
||||||
} else {
|
|
||||||
state = DisengagingIdler;
|
|
||||||
}
|
|
||||||
// in all cases disengage the idler
|
|
||||||
mm::motion.Idler(mm::Disengage);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case DisengagingIdler:
|
|
||||||
if (mm::motion.IdlerDisengaged()) {
|
|
||||||
state = AvoidingGrind;
|
|
||||||
// mm::motion.PlanMove(mm::Pulley, -100, 10); // @@TODO constants
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case AvoidingGrind: // state 3 move a little bit so it is not a grinded hole in filament
|
|
||||||
if (mm::motion.QueueEmpty()) {
|
|
||||||
state = Finishing;
|
|
||||||
mm::motion.Idler(mm::Disengage);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case Finishing:
|
|
||||||
if (mm::motion.QueueEmpty()) {
|
|
||||||
state = OK;
|
|
||||||
mm::motion.DisableAxis(mm::Pulley);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case ERR1DisengagingIdler: // couldn't unload to FINDA
|
|
||||||
if (mm::motion.IdlerDisengaged()) {
|
|
||||||
state = ERR1WaitingForUser;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case ERR1WaitingForUser: {
|
|
||||||
// waiting for user buttons and/or a command from the printer
|
|
||||||
bool help = modules::buttons::buttons.ButtonPressed(modules::buttons::Left) /*|| command_help()*/;
|
|
||||||
bool tryAgain = modules::buttons::buttons.ButtonPressed(modules::buttons::Middle) /*|| command_tryAgain()*/;
|
|
||||||
bool userResolved = modules::buttons::buttons.ButtonPressed(modules::buttons::Right) /*|| command_userResolved()*/;
|
|
||||||
if (help) {
|
|
||||||
// try to manually unload just a tiny bit - help the filament with the pulley
|
|
||||||
//@@TODO
|
|
||||||
} else if (tryAgain) {
|
|
||||||
// try again the whole sequence
|
|
||||||
Reset();
|
|
||||||
} else if (userResolved) {
|
|
||||||
// problem resolved - the user pulled the fillament by hand
|
|
||||||
// modules::leds::leds.SetMode(active_extruder, modules::leds::red, modules::leds::off);
|
|
||||||
// modules::leds::leds.SetMode(active_extruder, modules::leds::green, modules::leds::on);
|
|
||||||
// mm::motion.PlanMove(mm::Pulley, 450, 5000); // @@TODO constants
|
|
||||||
state = AvoidingGrind;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case OK:
|
|
||||||
// isFilamentLoaded = false; // filament unloaded
|
|
||||||
return true; // successfully finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @returns progress of operation
|
|
||||||
virtual uint8_t Progress() const override {
|
|
||||||
return state; // for simplicity return state, will be more elaborate later in order to report the exact state of the MMU into the printer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace logic
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/// @@TODO @3d-gussner
|
|
||||||
/// Extract the current state machines of high-level operations (load fillament, unload fillament etc.) here
|
|
||||||
/// Design some nice non-blocking API for these operations
|
|
||||||
///
|
|
||||||
/// Which automatons are high-level? Those which are being initiated either by a command over the serial line or from a button
|
|
||||||
/// - they report their progress to the printer
|
|
||||||
/// - they can be composed of other sub automatons
|
|
||||||
|
|
||||||
namespace logic {
|
|
||||||
|
|
||||||
/// Tasks derived from this base class are the top-level operations inhibited by the printer.
|
|
||||||
/// These tasks report their progress and only one of these tasks is allowed to run at once.
|
|
||||||
class TaskBase {
|
|
||||||
public:
|
|
||||||
inline TaskBase() = default;
|
|
||||||
|
|
||||||
virtual void Reset() = 0;
|
|
||||||
virtual bool Step() = 0;
|
|
||||||
/// probably individual states of the automaton
|
|
||||||
virtual uint8_t Progress() const = 0;
|
|
||||||
/// @@TODO cleanup status codes
|
|
||||||
/// @returns 0 if the operation is still running
|
|
||||||
/// 1 if the operation finished OK
|
|
||||||
/// >=2 if the operation failed - the value is the error code
|
|
||||||
virtual int8_t Status() const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
uint8_t state;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Logic {
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline Logic() = default;
|
|
||||||
|
|
||||||
void UnloadFilament();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace logic
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include "no_command.h"
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
NoCommand noCommand;
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "command_base.h"
|
||||||
|
#include "unload_to_finda.h"
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
/// A dummy No-command operation just to make the init of the firmware consistent (and cleaner code during processing)
|
||||||
|
class NoCommand : public CommandBase {
|
||||||
|
public:
|
||||||
|
inline NoCommand()
|
||||||
|
: CommandBase() {}
|
||||||
|
|
||||||
|
/// Restart the automaton
|
||||||
|
void Reset() override {}
|
||||||
|
|
||||||
|
/// @returns true if the state machine finished its job, false otherwise
|
||||||
|
bool Step() override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
extern NoCommand noCommand;
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// A complete set of progress codes which may be reported while running a high-level command/operation
|
||||||
|
/// This header file shall be included in the printer's firmware as well as a reference,
|
||||||
|
/// therefore the progress codes have been extracted to one place
|
||||||
|
|
||||||
|
enum class ProgressCode : uint_fast8_t {
|
||||||
|
OK = 0, ///< finished ok
|
||||||
|
|
||||||
|
/// Unload Filament related progress codes
|
||||||
|
EngagingIdler,
|
||||||
|
UnloadingToFinda,
|
||||||
|
DisengagingIdler,
|
||||||
|
AvoidingGrind,
|
||||||
|
FinishingMoves,
|
||||||
|
ERR1DisengagingIdler,
|
||||||
|
ERR1WaitingForUser
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
#include "unload_filament.h"
|
||||||
|
#include "../modules/buttons.h"
|
||||||
|
#include "../modules/finda.h"
|
||||||
|
#include "../modules/leds.h"
|
||||||
|
#include "../modules/motion.h"
|
||||||
|
#include "../modules/permanent_storage.h"
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
UnloadFilament unloadFilament;
|
||||||
|
|
||||||
|
void UnloadFilament::Reset() {
|
||||||
|
namespace mm = modules::motion;
|
||||||
|
// unloads filament from extruder - filament is above Bondtech gears
|
||||||
|
mm::motion.InitAxis(mm::Pulley);
|
||||||
|
state = ProgressCode::EngagingIdler;
|
||||||
|
error = ErrorCode::OK;
|
||||||
|
mm::motion.Idler(mm::Engage);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnloadFilament::Step() {
|
||||||
|
namespace mm = modules::motion;
|
||||||
|
switch (state) {
|
||||||
|
case ProgressCode::EngagingIdler: // state 1 engage idler
|
||||||
|
if (mm::motion.IdlerEngaged()) { // 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
|
||||||
|
if (unl.Step()) {
|
||||||
|
if (unl.state == UnloadToFinda::Failed) {
|
||||||
|
// couldn't unload to FINDA, report error and wait for user to resolve it
|
||||||
|
state = ProgressCode::ERR1DisengagingIdler;
|
||||||
|
// modules::leds::leds.SetMode(active_extruder, modules::leds::red, modules::leds::blink0);
|
||||||
|
} else {
|
||||||
|
state = ProgressCode::DisengagingIdler;
|
||||||
|
}
|
||||||
|
// in all cases disengage the idler
|
||||||
|
mm::motion.Idler(mm::Disengage);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case ProgressCode::DisengagingIdler:
|
||||||
|
if (mm::motion.IdlerDisengaged()) {
|
||||||
|
state = ProgressCode::AvoidingGrind;
|
||||||
|
// mm::motion.PlanMove(mm::Pulley, -100, 10); // @@TODO constants
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case ProgressCode::AvoidingGrind: // state 3 move a little bit so it is not a grinded hole in filament
|
||||||
|
if (mm::motion.QueueEmpty()) {
|
||||||
|
state = ProgressCode::FinishingMoves;
|
||||||
|
mm::motion.Idler(mm::Disengage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case ProgressCode::FinishingMoves:
|
||||||
|
if (mm::motion.QueueEmpty()) {
|
||||||
|
state = ProgressCode::OK;
|
||||||
|
mm::motion.DisableAxis(mm::Pulley);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case ProgressCode::ERR1DisengagingIdler: // couldn't unload to FINDA
|
||||||
|
error = ErrorCode::UNLOAD_FINDA_DIDNT_TRIGGER;
|
||||||
|
if (mm::motion.IdlerDisengaged()) {
|
||||||
|
state = ProgressCode::ERR1WaitingForUser;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case ProgressCode::ERR1WaitingForUser: {
|
||||||
|
// waiting for user buttons and/or a command from the printer
|
||||||
|
bool help = modules::buttons::buttons.ButtonPressed(modules::buttons::Left) /*|| command_help()*/;
|
||||||
|
bool tryAgain = modules::buttons::buttons.ButtonPressed(modules::buttons::Middle) /*|| command_tryAgain()*/;
|
||||||
|
bool userResolved = modules::buttons::buttons.ButtonPressed(modules::buttons::Right) /*|| command_userResolved()*/;
|
||||||
|
if (help) {
|
||||||
|
// try to manually unload just a tiny bit - help the filament with the pulley
|
||||||
|
//@@TODO
|
||||||
|
} else if (tryAgain) {
|
||||||
|
// try again the whole sequence
|
||||||
|
Reset();
|
||||||
|
} else if (userResolved) {
|
||||||
|
// problem resolved - the user pulled the fillament by hand
|
||||||
|
// modules::leds::leds.SetMode(active_extruder, modules::leds::red, modules::leds::off);
|
||||||
|
// modules::leds::leds.SetMode(active_extruder, modules::leds::green, modules::leds::on);
|
||||||
|
// mm::motion.PlanMove(mm::Pulley, 450, 5000); // @@TODO constants
|
||||||
|
state = ProgressCode::AvoidingGrind;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case ProgressCode::OK:
|
||||||
|
// isFilamentLoaded = false; // filament unloaded
|
||||||
|
return true; // successfully finished
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "command_base.h"
|
||||||
|
#include "unload_to_finda.h"
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
/// A high-level command state machine
|
||||||
|
/// Handles the complex logic of unloading filament
|
||||||
|
class UnloadFilament : public CommandBase {
|
||||||
|
public:
|
||||||
|
inline UnloadFilament()
|
||||||
|
: CommandBase()
|
||||||
|
, unl(3) { Reset(); }
|
||||||
|
|
||||||
|
/// Restart the automaton
|
||||||
|
void Reset() override;
|
||||||
|
|
||||||
|
/// @returns true if the state machine finished its job, false otherwise
|
||||||
|
bool Step() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
UnloadToFinda unl;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern UnloadFilament unloadFilament;
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
#include "unload_to_finda.h"
|
||||||
|
#include "../modules/motion.h"
|
||||||
|
#include "../modules/leds.h"
|
||||||
|
#include "../modules/buttons.h"
|
||||||
|
#include "../modules/finda.h"
|
||||||
|
#include "../modules/permanent_storage.h"
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
void UnloadToFinda::Reset() {
|
||||||
|
namespace mm = modules::motion;
|
||||||
|
namespace mf = modules::finda;
|
||||||
|
// check the inital state of FINDA and plan the moves
|
||||||
|
if (mf::finda.Pressed()) {
|
||||||
|
state = OK; // FINDA is already off, we assume the fillament is not there, i.e. already unloaded
|
||||||
|
} else {
|
||||||
|
// FINDA is sensing the filament, plan moves to unload it
|
||||||
|
int unloadSteps = /*BowdenLength::get() +*/ 1100; // @@TODO
|
||||||
|
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
|
||||||
|
state = WaitingForFINDA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnloadToFinda::Step() {
|
||||||
|
namespace mm = modules::motion;
|
||||||
|
namespace mf = modules::finda;
|
||||||
|
switch (state) {
|
||||||
|
case WaitingForFINDA:
|
||||||
|
if (modules::finda::finda.Pressed()) {
|
||||||
|
// detected end of filament
|
||||||
|
state = OK;
|
||||||
|
} else if (/*tmc2130_read_gstat() &&*/ mm::motion.QueueEmpty()) {
|
||||||
|
// we reached the end of move queue, but the FINDA didn't switch off
|
||||||
|
// two possible causes - grinded filament of malfunctioning FINDA
|
||||||
|
if (--maxTries) {
|
||||||
|
Reset(); // try again
|
||||||
|
} else {
|
||||||
|
state = Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case OK:
|
||||||
|
case Failed:
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// Unload to FINDA "small" state machine
|
||||||
|
/// "small" state machines will serve as building blocks for high-level commands/operations
|
||||||
|
/// - engage/disengage idler
|
||||||
|
/// - rotate pulley to some direction as long as the FINDA is on/off
|
||||||
|
/// - rotate some axis to some fixed direction
|
||||||
|
/// - load/unload to finda
|
||||||
|
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
/// A "small" automaton example - Try to unload filament to FINDA and if it fails try to recover several times.
|
||||||
|
/// \dot
|
||||||
|
/// digraph example {
|
||||||
|
/// node [shape=record, fontname=Helvetica, fontsize=10];
|
||||||
|
/// b [ label="class B" URL="\ref B"];
|
||||||
|
/// c [ label="class C" URL="\ref C"];
|
||||||
|
/// b -> c [ arrowhead="open", style="dashed" ];
|
||||||
|
///}
|
||||||
|
///\enddot
|
||||||
|
struct UnloadToFinda {
|
||||||
|
enum {
|
||||||
|
WaitingForFINDA,
|
||||||
|
OK,
|
||||||
|
Failed
|
||||||
|
};
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t maxTries;
|
||||||
|
inline UnloadToFinda(uint8_t maxTries)
|
||||||
|
: maxTries(maxTries) { Reset(); }
|
||||||
|
|
||||||
|
/// Restart the automaton
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
/// @returns true if the state machine finished its job, false otherwise
|
||||||
|
bool Step();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
28
src/main.cpp
28
src/main.cpp
|
|
@ -13,14 +13,14 @@
|
||||||
#include "modules/leds.h"
|
#include "modules/leds.h"
|
||||||
#include "modules/protocol.h"
|
#include "modules/protocol.h"
|
||||||
|
|
||||||
#include "logic/mm_control.h"
|
#include "logic/command_base.h"
|
||||||
|
#include "logic/no_command.h"
|
||||||
|
#include "logic/unload_filament.h"
|
||||||
|
|
||||||
static modules::protocol::Protocol protocol;
|
static modules::protocol::Protocol protocol;
|
||||||
//static modules::buttons::Buttons buttons;
|
|
||||||
//static modules::leds::LEDs leds;
|
|
||||||
|
|
||||||
// @@TODO we need a dummy noCommand to init the pointer with ... makes the rest of the code much better and safer
|
logic::CommandBase *currentCommand = &logic::noCommand;
|
||||||
logic::TaskBase *currentCommand = nullptr;
|
|
||||||
/// remember the request message that started the currently running command
|
/// remember the request message that started the currently running command
|
||||||
modules::protocol::RequestMsg currentCommandRq(modules::protocol::RequestMsgCodes::unknown, 0);
|
modules::protocol::RequestMsg currentCommandRq(modules::protocol::RequestMsgCodes::unknown, 0);
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ void SendMessage(const modules::protocol::ResponseMsg &msg) {
|
||||||
|
|
||||||
void PlanCommand(const modules::protocol::RequestMsg &rq) {
|
void PlanCommand(const modules::protocol::RequestMsg &rq) {
|
||||||
namespace mp = modules::protocol;
|
namespace mp = modules::protocol;
|
||||||
if ((currentCommand == nullptr) || (currentCommand->Status() == 1)) {
|
if (currentCommand->Error() == ErrorCode::OK) {
|
||||||
// we are allowed to start a new command
|
// we are allowed to start a new command
|
||||||
switch (rq.code) {
|
switch (rq.code) {
|
||||||
case mp::RequestMsgCodes::Cut:
|
case mp::RequestMsgCodes::Cut:
|
||||||
|
|
@ -137,7 +137,7 @@ void PlanCommand(const modules::protocol::RequestMsg &rq) {
|
||||||
// currentCommand = &toolCommand;
|
// currentCommand = &toolCommand;
|
||||||
break;
|
break;
|
||||||
case mp::RequestMsgCodes::Unload:
|
case mp::RequestMsgCodes::Unload:
|
||||||
// currentCommand = &unloadCommand;
|
currentCommand = &logic::unloadFilament;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// currentCommand = &noCommand;
|
// currentCommand = &noCommand;
|
||||||
|
|
@ -149,27 +149,23 @@ void PlanCommand(const modules::protocol::RequestMsg &rq) {
|
||||||
|
|
||||||
void ReportRunningCommand() {
|
void ReportRunningCommand() {
|
||||||
namespace mp = modules::protocol;
|
namespace mp = modules::protocol;
|
||||||
if (!currentCommand) {
|
|
||||||
// @@TODO what to report after startup?
|
|
||||||
} else {
|
|
||||||
mp::ResponseMsgParamCodes commandStatus;
|
mp::ResponseMsgParamCodes commandStatus;
|
||||||
uint8_t value = 0;
|
uint8_t value = 0;
|
||||||
switch (currentCommand->Status()) {
|
switch (currentCommand->Error()) {
|
||||||
case 0:
|
case ErrorCode::RUNNING:
|
||||||
commandStatus = mp::ResponseMsgParamCodes::Processing;
|
commandStatus = mp::ResponseMsgParamCodes::Processing;
|
||||||
value = currentCommand->Progress();
|
value = (uint8_t)currentCommand->State();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case ErrorCode::OK:
|
||||||
commandStatus = mp::ResponseMsgParamCodes::Finished;
|
commandStatus = mp::ResponseMsgParamCodes::Finished;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
commandStatus = mp::ResponseMsgParamCodes::Error;
|
commandStatus = mp::ResponseMsgParamCodes::Error;
|
||||||
value = currentCommand->Status() - 2; // @@TODO cleanup
|
value = (uint8_t)currentCommand->Error();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SendMessage(mp::ResponseMsg(currentCommandRq, commandStatus, value));
|
SendMessage(mp::ResponseMsg(currentCommandRq, commandStatus, value));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessRequestMsg(const modules::protocol::RequestMsg &rq) {
|
void ProcessRequestMsg(const modules::protocol::RequestMsg &rq) {
|
||||||
namespace mp = modules::protocol;
|
namespace mp = modules::protocol;
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ public:
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
/// @returns true if all planned moves have been finished
|
/// @returns true if all planned moves have been finished
|
||||||
bool QueueEmpty() const;
|
bool QueueEmpty() const { return false; }
|
||||||
|
|
||||||
/// stop whatever moves are being done
|
/// stop whatever moves are being done
|
||||||
void AbortPlannedMoves() {}
|
void AbortPlannedMoves() {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue