Add bit masks for error codes for the TMC drivers

+ add common error handling for idler and selector
+ improve error handling in command_base
+ rename ERR1xxxx errors to ERRxxxx (remove the '1')
pull/78/head
D.R.racer 2021-07-27 08:47:51 +02:00 committed by DRracer
parent df2c1ba7fe
commit 751ee46450
25 changed files with 236 additions and 173 deletions

View File

@ -1,25 +1,25 @@
#include "command_base.h" #include "command_base.h"
#include "../modules/idler.h" #include "../modules/idler.h"
#include "../modules/selector.h"
namespace mi = modules::idler; namespace mi = modules::idler;
namespace ms = modules::selector;
namespace logic { namespace logic {
bool CommandBase::Step() { bool CommandBase::Step() {
// check the global HW errors - may be we should avoid the modules layer and check for the HAL layer errors directly // check the global HW errors - may be we should avoid the modules layer and check for the HAL layer errors directly
// @@TODO discuss... if (mi::idler.State() == mi::Idler::Failed) {
bool any_error = mi::idler.State() == mi::Idler::Failed; state = ProgressCode::ERRTMCFailed;
error = ErrorCode::TMC_IOIN_MISMATCH;
// @@TODO check all other HW issues here to be able to respond with the appropriate error code into the printer return true; // the HW error prevents us from continuing with the with the state machine - the MMU must be restarted/fixed before continuing
} else if (ms::selector.State() == ms::Selector::Failed) {
if (any_error) { state = ProgressCode::ERRTMCFailed;
state = ProgressCode::ERR1TMCInitFailed; error = ErrorCode::TMC_IOIN_MISMATCH;
error = ErrorCode::TMC_INIT_ERROR; return true; // the HW error prevents us from continuing with the with the state machine - the MMU must be restarted/fixed before continuing
return true; // the HW error prevents us from continuing with the with the state machine
// the MMU must be restarted/fixed before continuing
} else {
return StepInner();
} }
return StepInner();
} }
} // namespace logic } // namespace logic

View File

@ -1,25 +1,54 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
/// A complete set of error codes which may be a result of a high-level command/operation /// 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, /// 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 /// therefore the error codes have been extracted to one place.
enum class ErrorCode : int_fast8_t { ///
RUNNING = 0, ///< the operation is still running /// Please note the errors are intentionally coded as "negative" values (highest bit set),
OK, ///< the operation finished OK /// becase they are a complement to reporting the state of the high-level state machines -
/// positive values are considered as normal progress, negative values are errors.
///
/// Please note, that multiple TMC errors can occur at once, thus they are defined as a bitmask of the higher byte.
/// Also, as there are 3 TMC drivers on the board, each error is added a bit for the corresponding TMC -
/// TMC_PULLEY_BIT, TMC_SELECTOR_BIT, TMC_IDLER_BIT,
/// The resulting error is a bitwise OR over 3 TMC drivers and their status, which should cover most of the situations correctly.
enum class ErrorCode : uint_fast16_t {
RUNNING = 0x0000, ///< the operation is still running
OK = 0x0001, ///< the operation finished OK
/// Unload Filament related error codes /// Unload Filament related error codes
FINDA_DIDNT_SWITCH_ON = -1, ///< FINDA didn't switch on while loading filament - either there is something blocking the metal ball or a cable is broken/disconnected FINDA_DIDNT_SWITCH_ON = 0x8001, ///< FINDA didn't switch on while loading filament - either there is something blocking the metal ball or a cable is broken/disconnected
FINDA_DIDNT_SWITCH_OFF = -2, ///< FINDA didn't switch off while unloading filament FINDA_DIDNT_SWITCH_OFF = 0x8002, ///< FINDA didn't switch off while unloading filament
FSENSOR_DIDNT_SWITCH_ON = -3, ///< Filament sensor didn't switch on while performing LoadFilament FSENSOR_DIDNT_SWITCH_ON = 0x8003, ///< Filament sensor didn't switch on while performing LoadFilament
FSENSOR_DIDNT_SWITCH_OFF = -4, ///< Filament sensor didn't switch off while performing UnloadFilament FSENSOR_DIDNT_SWITCH_OFF = 0x8004, ///< Filament sensor didn't switch off while performing UnloadFilament
FILAMENT_ALREADY_LOADED = -5, ///< cannot perform operation LoadFilament or move the selector as the filament is already loaded FILAMENT_ALREADY_LOADED = 0x8005, ///< cannot perform operation LoadFilament or move the selector as the filament is already loaded
TMC_INIT_ERROR = -6, ///< TMC driver init error - the MMU cannot move one motor due to a HW problem MMU_NOT_RESPONDING = 0x807e, ///< internal error of the printer - communication with the MMU is not working
MMU_NOT_RESPONDING = -126, ///< internal error of the printer - communication with the MMU is not working INTERNAL = 0x807f, ///< internal runtime error (software)
INTERNAL = -127, ///< internal runtime error (software) // TMC bit masks
TMC_PULLEY_BIT = 0x0080, ///< TMC Pulley bit
TMC_SELECTOR_BIT = 0x00A0, ///< TMC Pulley bit
TMC_IDLER_BIT = 0x00C0, ///< TMC Pulley bit
TMC_IOIN_MISMATCH = 0x8100, ///< TMC driver init error - TMC dead or bad communication
/// TMC driver reset - recoverable, we just need to rehome the axis
/// Idler: can be rehomed any time
/// Selector: if there is a filament, remove it and rehome, if there is no filament, just rehome
/// Pulley: do nothing - for the loading sequence - just restart and move slowly, for the unload sequence just restart
TMC_RESET = 0x8200,
TMC_UNDERVOLTAGE_ON_CHARGE_PUMP = 0x8400, ///< not enough current for the TMC, NOT RECOVERABLE
TMC_SERIOUS_ERROR = 0x8800, ///< TMC driver serious error coil A or coil B - dangerous to recover
TMC_ERROR_A = 0x9000, ///< TMC driver error coil A or coil B - can be recovered
TMC_OVER_TEMPERATURE_WARN = 0xA000, ///< TMC driver over temperature warning - can be recovered by restarting the driver
TMC_OVER_TEMPERATURE_ERROR = 0xC000, ///< TMC driver over temperature error - the TMC probably fried
}; };

View File

@ -42,7 +42,7 @@ bool LoadFilament::StepInner() {
if (feed.Step()) { if (feed.Step()) {
if (feed.State() == FeedToFinda::Failed) { if (feed.State() == FeedToFinda::Failed) {
// @@TODO - try to repeat 6x - push/pull sequence - probably something to put into feed_to_finda as an option // @@TODO - try to repeat 6x - push/pull sequence - probably something to put into feed_to_finda as an option
state = ProgressCode::ERR1DisengagingIdler; state = ProgressCode::ERRDisengagingIdler;
error = ErrorCode::FINDA_DIDNT_SWITCH_ON; error = ErrorCode::FINDA_DIDNT_SWITCH_ON;
mi::idler.Disengage(); mi::idler.Disengage();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::Mode::off); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::Mode::off);
@ -74,18 +74,18 @@ bool LoadFilament::StepInner() {
break; break;
case ProgressCode::OK: case ProgressCode::OK:
return true; return true;
case ProgressCode::ERR1DisengagingIdler: // couldn't unload to FINDA case ProgressCode::ERRDisengagingIdler: // couldn't unload to FINDA
if (!mi::idler.Engaged()) { if (!mi::idler.Engaged()) {
state = ProgressCode::ERR1WaitingForUser; state = ProgressCode::ERRWaitingForUser;
mu::userInput.Clear(); // remove all buffered events if any just before we wait for some input mu::userInput.Clear(); // remove all buffered events if any just before we wait for some input
} }
return false; return false;
case ProgressCode::ERR1WaitingForUser: { case ProgressCode::ERRWaitingForUser: {
// waiting for user buttons and/or a command from the printer // waiting for user buttons and/or a command from the printer
mu::Event ev = mu::userInput.ConsumeEvent(); mu::Event ev = mu::userInput.ConsumeEvent();
switch (ev) { switch (ev) {
case mu::Event::Left: // try to manually load just a tiny bit - help the filament with the pulley case mu::Event::Left: // try to manually load just a tiny bit - help the filament with the pulley
state = ProgressCode::ERR1EngagingIdler; state = ProgressCode::ERREngagingIdler;
mi::idler.Engage(mg::globals.ActiveSlot()); mi::idler.Engage(mg::globals.ActiveSlot());
break; break;
case mu::Event::Middle: // try again the whole sequence case mu::Event::Middle: // try again the whole sequence
@ -102,20 +102,20 @@ bool LoadFilament::StepInner() {
} }
return false; return false;
} }
case ProgressCode::ERR1EngagingIdler: case ProgressCode::ERREngagingIdler:
if (mi::idler.Engaged()) { if (mi::idler.Engaged()) {
state = ProgressCode::ERR1HelpingFilament; state = ProgressCode::ERRHelpingFilament;
mm::motion.PlanMove(mm::Pulley, 450, 5000); //@@TODO constants mm::motion.PlanMove(mm::Pulley, 450, 5000); //@@TODO constants
} }
return false; return false;
case ProgressCode::ERR1HelpingFilament: case ProgressCode::ERRHelpingFilament:
if (mf::finda.Pressed()) { if (mf::finda.Pressed()) {
// the help was enough to press the FINDA, we are ok, continue normally // the help was enough to press the FINDA, we are ok, continue normally
state = ProgressCode::FeedingToBondtech; state = ProgressCode::FeedingToBondtech;
error = ErrorCode::OK; error = ErrorCode::OK;
} else if (mm::motion.QueueEmpty()) { } else if (mm::motion.QueueEmpty()) {
// helped a bit, but FINDA didn't trigger, return to the main error state // helped a bit, but FINDA didn't trigger, return to the main error state
state = ProgressCode::ERR1DisengagingIdler; state = ProgressCode::ERRDisengagingIdler;
} }
return false; return false;
default: // we got into an unhandled state, better report it default: // we got into an unhandled state, better report it

View File

@ -16,12 +16,12 @@ enum class ProgressCode : uint_fast8_t {
AvoidingGrind, AvoidingGrind,
FinishingMoves, FinishingMoves,
ERR1DisengagingIdler, ERRDisengagingIdler,
ERR1EngagingIdler, ERREngagingIdler,
ERR1WaitingForUser, ERRWaitingForUser,
ERRInternal, ERRInternal,
ERR1HelpingFilament, ERRHelpingFilament,
ERR1TMCInitFailed, ERRTMCFailed,
UnloadingFilament, UnloadingFilament,
LoadingFilament, LoadingFilament,

View File

@ -35,7 +35,7 @@ bool UnloadFilament::StepInner() {
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::ERRDisengagingIdler;
error = ErrorCode::FINDA_DIDNT_SWITCH_OFF; error = ErrorCode::FINDA_DIDNT_SWITCH_OFF;
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); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
@ -68,18 +68,18 @@ bool UnloadFilament::StepInner() {
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on); ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
} }
return false; return false;
case ProgressCode::ERR1DisengagingIdler: // couldn't unload to FINDA case ProgressCode::ERRDisengagingIdler: // couldn't unload to FINDA
if (!mi::idler.Engaged()) { if (!mi::idler.Engaged()) {
state = ProgressCode::ERR1WaitingForUser; state = ProgressCode::ERRWaitingForUser;
mu::userInput.Clear(); // remove all buffered events if any just before we wait for some input mu::userInput.Clear(); // remove all buffered events if any just before we wait for some input
} }
return false; return false;
case ProgressCode::ERR1WaitingForUser: { case ProgressCode::ERRWaitingForUser: {
// waiting for user buttons and/or a command from the printer // waiting for user buttons and/or a command from the printer
mu::Event ev = mu::userInput.ConsumeEvent(); mu::Event ev = mu::userInput.ConsumeEvent();
switch (ev) { switch (ev) {
case mu::Event::Left: // try to manually unload just a tiny bit - help the filament with the pulley case mu::Event::Left: // try to manually unload just a tiny bit - help the filament with the pulley
state = ProgressCode::ERR1EngagingIdler; state = ProgressCode::ERREngagingIdler;
mi::idler.Engage(mg::globals.ActiveSlot()); mi::idler.Engage(mg::globals.ActiveSlot());
break; break;
case mu::Event::Middle: // try again the whole sequence case mu::Event::Middle: // try again the whole sequence
@ -96,20 +96,20 @@ bool UnloadFilament::StepInner() {
} }
return false; return false;
} }
case ProgressCode::ERR1EngagingIdler: case ProgressCode::ERREngagingIdler:
if (mi::idler.Engaged()) { if (mi::idler.Engaged()) {
state = ProgressCode::ERR1HelpingFilament; state = ProgressCode::ERRHelpingFilament;
mm::motion.PlanMove(mm::Pulley, 450, 5000); mm::motion.PlanMove(mm::Pulley, 450, 5000);
} }
return false; return false;
case ProgressCode::ERR1HelpingFilament: case ProgressCode::ERRHelpingFilament:
if (!mf::finda.Pressed()) { if (!mf::finda.Pressed()) {
// the help was enough to depress the FINDA, we are ok, continue normally // the help was enough to depress the FINDA, we are ok, continue normally
state = ProgressCode::DisengagingIdler; state = ProgressCode::DisengagingIdler;
error = ErrorCode::OK; error = ErrorCode::OK;
} else if (mm::motion.QueueEmpty()) { } else if (mm::motion.QueueEmpty()) {
// helped a bit, but FINDA didn't trigger, return to the main error state // helped a bit, but FINDA didn't trigger, return to the main error state
state = ProgressCode::ERR1DisengagingIdler; state = ProgressCode::ERRDisengagingIdler;
} }
return false; return false;
case ProgressCode::OK: case ProgressCode::OK:

View File

@ -207,18 +207,18 @@ void ReportVersion(const mp::RequestMsg &rq) {
void ReportRunningCommand() { void ReportRunningCommand() {
mp::ResponseMsgParamCodes commandStatus; mp::ResponseMsgParamCodes commandStatus;
uint8_t value = 0; uint16_t value = 0;
switch (currentCommand->Error()) { switch (currentCommand->Error()) {
case ErrorCode::RUNNING: case ErrorCode::RUNNING:
commandStatus = mp::ResponseMsgParamCodes::Processing; commandStatus = mp::ResponseMsgParamCodes::Processing;
value = (uint8_t)currentCommand->State(); value = (uint16_t)currentCommand->State();
break; break;
case ErrorCode::OK: case ErrorCode::OK:
commandStatus = mp::ResponseMsgParamCodes::Finished; commandStatus = mp::ResponseMsgParamCodes::Finished;
break; break;
default: default:
commandStatus = mp::ResponseMsgParamCodes::Error; commandStatus = mp::ResponseMsgParamCodes::Error;
value = (uint8_t)currentCommand->Error(); value = (uint16_t)currentCommand->Error();
break; break;
} }

View File

@ -9,6 +9,7 @@ target_sources(
idler.cpp idler.cpp
leds.cpp leds.cpp
motion.cpp motion.cpp
movable_base.cpp
permanent_storage.cpp permanent_storage.cpp
selector.cpp selector.cpp
timebase.cpp timebase.cpp

View File

@ -11,57 +11,34 @@ Idler idler;
namespace mm = modules::motion; namespace mm = modules::motion;
Idler::EngageDisengage Idler::Disengage() { void Idler::PrepareMoveToPlannedSlot() {
if (state == Moving) mm::motion.PlanMoveTo<mm::Idler>(SlotPosition(plannedSlot), 1000._I_deg_s); // @@TODO
return EngageDisengage::Refused; }
Idler::OperationResult Idler::Disengage() {
if (state == Moving)
return OperationResult::Refused;
plannedSlot = IdleSlotIndex();
plannedEngage = false; plannedEngage = false;
if (!Engaged()) if (!Engaged())
return true; return OperationResult::Accepted;
mm::motion.InitAxis(mm::Idler); return InitMovement(mm::Idler);
// plan move to idle position
mm::motion.PlanMoveTo<mm::Idler>(SlotPosition(IdleSlotIndex()), 1000._I_deg_s); // @@TODO
state = Moving;
return true;
// return EngageDisengage::Accepted;
//
// if (!mm::motion.InitAxis(mm::Idler)) {
// state = Failed;
// return EngageDisengage::Failed;
// } else {
// // plan move to idle position
// mm::motion.PlanMove(mm::Idler, config::idlerSlotPositions[IdleSlotIndex()] - mm::motion.Position(mm::Idler), 1000); // @@TODO
// state = Moving;
// return EngageDisengage::Accepted;
// }
} }
Idler::EngageDisengage Idler::Engage(uint8_t slot) { Idler::OperationResult Idler::Engage(uint8_t slot) {
if (state == Moving) if (state == Moving)
return EngageDisengage::Refused; return OperationResult::Refused;
plannedSlot = slot; plannedSlot = slot;
plannedEngage = true; plannedEngage = true;
if (Engaged()) if (Engaged())
return true; return OperationResult::Accepted;
mm::motion.InitAxis(mm::Idler); return InitMovement(mm::Idler);
mm::motion.PlanMoveTo<mm::Idler>(SlotPosition(slot), 1000._I_deg_s); // @@TODO
state = Moving;
return true;
// return EngageDisengage::Accepted;
//
// if (!mm::motion.InitAxis(mm::Idler)) {
// state = Failed;
// return EngageDisengage::Failed;
// } else {
// mm::motion.PlanMove(mm::Idler, config::idlerSlotPositions[slot] - mm::motion.Position(mm::Idler), 1000); // @@TODO
// state = Moving;
// return EngageDisengage::Accepted;
// }
} }
bool Idler::Home() { bool Idler::Home() {

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "../config/config.h" #include "../config/config.h"
#include "../modules/axisunit.h" #include "axisunit.h"
#include "movable_base.h"
namespace modules { namespace modules {
@ -10,37 +11,21 @@ namespace idler {
namespace mm = modules::motion; namespace mm = modules::motion;
/// The Idler model handles asynchronnous Engaging / Disengaging operations and keeps track of idler's current state. /// The Idler model handles asynchronnous Engaging / Disengaging operations and keeps track of idler's current state.
class Idler { class Idler : public motion::MovableBase {
public: public:
/// Internal states of idler's state machine
enum {
Ready = 0, // intentionally set as zero in order to allow zeroing the Idler structure upon startup -> avoid explicit initialization code
Moving,
Failed
};
inline constexpr Idler() inline constexpr Idler()
: state(Ready) : MovableBase()
, plannedEngage(false) , plannedEngage(false)
, plannedSlot(0)
, currentSlot(0)
, currentlyEngaged(false) {} , currentlyEngaged(false) {}
/// Engage/Disengage return values
enum class EngageDisengage : uint8_t {
Accepted, ///< the operation has been successfully started
Refused, ///< another operation is currently underway, cannot start a new one
Failed ///< the operation could not been started due to HW issues
};
/// Plan engaging of the idler to a specific filament slot /// Plan engaging of the idler to a specific filament slot
/// @param slot index to be activated /// @param slot index to be activated
/// @returns #EngageDisengage /// @returns #EngageDisengage
EngageDisengage Engage(uint8_t slot); OperationResult Engage(uint8_t slot);
/// Plan disengaging of the idler, i.e. parking the idler /// Plan disengaging of the idler, i.e. parking the idler
/// @returns #EngageDisengage /// @returns #EngageDisengage
EngageDisengage Disengage(); OperationResult Disengage();
/// Plan homing of the idler axis /// Plan homing of the idler axis
/// @returns false in case an operation is already underway /// @returns false in case an operation is already underway
@ -55,9 +40,6 @@ public:
/// 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; }
/// @returns predefined positions of individual slots /// @returns predefined positions of individual slots
static constexpr mm::I_pos_t SlotPosition(uint8_t slot) { static constexpr mm::I_pos_t SlotPosition(uint8_t slot) {
return mm::unitToAxisUnit<mm::I_pos_t>(config::idlerSlotPositions[slot]); return mm::unitToAxisUnit<mm::I_pos_t>(config::idlerSlotPositions[slot]);
@ -66,19 +48,14 @@ public:
/// @returns the index of idle position of the idler, usually 5 in case of 0-4 valid indices of filament slots /// @returns the index of idle position of the idler, usually 5 in case of 0-4 valid indices of filament slots
inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; } inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; }
/// @returns internal state of the Idler protected:
inline uint8_t State() const { return state; } virtual void PrepareMoveToPlannedSlot() override;
private: private:
/// internal state of the automaton
uint8_t state;
/// direction of travel - engage/disengage /// direction of travel - engage/disengage
bool plannedEngage; bool plannedEngage;
uint8_t plannedSlot;
/// current state /// current state
uint8_t currentSlot;
bool currentlyEngaged; bool currentlyEngaged;
}; };

View File

@ -0,0 +1,19 @@
#include "movable_base.h"
#include "motion.h"
namespace modules {
namespace motion {
MovableBase::OperationResult MovableBase::InitMovement(config::Axis axis) {
if (motion.InitAxis(axis)) {
PrepareMoveToPlannedSlot();
state = Moving;
return OperationResult::Accepted;
} else {
state = Failed;
return OperationResult::Failed;
}
}
} // namespace motion
} // namespace modules

View File

@ -0,0 +1,56 @@
#pragma once
#include <stdint.h>
#include "../config/axis.h"
namespace modules {
namespace motion {
/// Base class for movable modules - #Idler and #Selector contains the common code
class MovableBase {
public:
/// Internal states of the state machine
enum {
Ready = 0, // intentionally set as zero in order to allow zeroing the Idler structure upon startup -> avoid explicit initialization code
Moving,
Failed
};
/// Operation (Engage/Disengage/MoveToSlot) return values
enum class OperationResult : uint8_t {
Accepted, ///< the operation has been successfully started
Refused, ///< another operation is currently underway, cannot start a new one
Failed ///< the operation could not been started due to HW issues
};
inline constexpr MovableBase()
: state(Ready)
, plannedSlot(0)
, currentSlot(0) {}
/// virtual ~MovableBase(); intentionally disabled, see description in logic::CommandBase
/// @returns currently active slot
/// 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 derive class (idler/selector)
inline uint8_t Slot() const { return currentSlot; }
/// @returns internal state of the state machine
inline uint8_t State() const { return state; }
protected:
/// internal state of the automaton
uint8_t state;
/// planned slot - where to move to
uint8_t plannedSlot;
/// current slot
uint8_t currentSlot;
virtual void PrepareMoveToPlannedSlot() = 0;
OperationResult InitMovement(config::Axis axis);
};
} // namespace motion
} // namespace modules

View File

@ -197,7 +197,7 @@ uint8_t Protocol::EncodeResponseVersion(const RequestMsg &msg, uint8_t value, ui
return dst - txbuff + 1; return dst - txbuff + 1;
} }
uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint8_t value, uint8_t *txbuff) { uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint16_t value, uint8_t *txbuff) {
txbuff[0] = (uint8_t)msg.code; txbuff[0] = (uint8_t)msg.code;
txbuff[1] = msg.value + '0'; txbuff[1] = msg.value + '0';
txbuff[2] = ' '; txbuff[2] = ' ';
@ -209,10 +209,21 @@ uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMs
} else if (value < 100) { } else if (value < 100) {
*dst++ = value / 10 + '0'; *dst++ = value / 10 + '0';
*dst++ = value % 10 + '0'; *dst++ = value % 10 + '0';
} else { } else if (value < 1000) {
*dst++ = value / 100 + '0'; *dst++ = value / 100 + '0';
*dst++ = (value / 10) % 10 + '0'; *dst++ = (value / 10) % 10 + '0';
*dst++ = value % 10 + '0'; *dst++ = value % 10 + '0';
} else if (value < 10000) {
*dst++ = value / 1000 + '0';
*dst++ = (value / 100) % 100 + '0';
*dst++ = (value / 10) % 10 + '0';
*dst++ = value % 10 + '0';
} else {
*dst++ = value / 10000 + '0';
*dst++ = (value / 1000) % 1000 + '0';
*dst++ = (value / 100) % 100 + '0';
*dst++ = (value / 10) % 10 + '0';
*dst++ = value % 10 + '0';
} }
} }
*dst = '\n'; *dst = '\n';

View File

@ -52,12 +52,12 @@ struct RequestMsg {
struct ResponseMsg { struct ResponseMsg {
RequestMsg request; ///< response is always preceeded by the request message RequestMsg request; ///< response is always preceeded by the request message
ResponseMsgParamCodes paramCode; ///< code of the parameter ResponseMsgParamCodes paramCode; ///< code of the parameter
uint8_t paramValue; ///< value of the parameter uint16_t paramValue; ///< value of the parameter
/// @param request the source request message this response is a reply to /// @param request the source request message this response is a reply to
/// @param paramCode code of the parameter /// @param paramCode code of the parameter
/// @param paramValue value of the parameter /// @param paramValue value of the parameter
inline ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint8_t paramValue) inline ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint16_t paramValue)
: request(request) : request(request)
, paramCode(paramCode) , paramCode(paramCode)
, paramValue(paramValue) {} , paramValue(paramValue) {}
@ -123,7 +123,7 @@ public:
/// @param value related to status of operation(e.g. error code or progress) /// @param value related to status of operation(e.g. error code or progress)
/// @param txbuff where to format the message /// @param txbuff where to format the message
/// @returns number of bytes written into txbuff /// @returns number of bytes written into txbuff
static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint8_t value, uint8_t *txbuff); static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint16_t value, uint8_t *txbuff);
/// @returns the most recently lexed request message /// @returns the most recently lexed request message
inline const RequestMsg GetRequestMsg() const { return requestMsg; } inline const RequestMsg GetRequestMsg() const { return requestMsg; }

View File

@ -11,19 +11,20 @@ Selector selector;
namespace mm = modules::motion; namespace mm = modules::motion;
bool Selector::MoveToSlot(uint8_t slot) { void Selector::PrepareMoveToPlannedSlot() {
mm::motion.PlanMoveTo<mm::Selector>(SlotPosition(plannedSlot), 1000.0_S_mm_s); // @@TODO
}
Selector::OperationResult Selector::MoveToSlot(uint8_t slot) {
if (state == Moving) if (state == Moving)
return false; return OperationResult::Refused;
plannedSlot = slot; plannedSlot = slot;
if (currentSlot == slot) if (currentSlot == slot)
return true; return OperationResult::Accepted;
mm::motion.InitAxis(mm::Selector); return InitMovement(mm::Selector);
mm::motion.PlanMoveTo<mm::Selector>(SlotPosition(slot), 1000.0_S_mm_s); // @@TODO
state = Moving;
return true;
} }
bool Selector::Home() { bool Selector::Home() {

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "../config/config.h" #include "../config/config.h"
#include "../modules/axisunit.h" #include "axisunit.h"
#include "movable_base.h"
namespace modules { namespace modules {
@ -10,24 +11,15 @@ namespace selector {
namespace mm = modules::motion; namespace mm = modules::motion;
/// The selector model - handles asynchronnous move operations between filament individual slots and keeps track of selector's current state. /// The selector model - handles asynchronnous move operations between filament individual slots and keeps track of selector's current state.
class Selector { class Selector : public mm::MovableBase {
public: public:
/// Internal states of selector's state machine
enum {
Ready = 0,
Moving,
Failed
};
inline constexpr Selector() inline constexpr Selector()
: state(Ready) : MovableBase() {}
, plannedSlot(0)
, currentSlot(0) {}
/// Plan move of the selector to a specific filament slot /// Plan move of the selector to a specific filament slot
/// @param slot index to move to /// @param slot index to move to
/// @returns false in case an operation is already underway /// @returns false in case an operation is already underway
bool MoveToSlot(uint8_t slot); OperationResult MoveToSlot(uint8_t slot);
/// Plan homing of the selector's axis /// Plan homing of the selector's axis
/// @returns false in case an operation is already underway /// @returns false in case an operation is already underway
@ -37,11 +29,6 @@ public:
/// @returns true if the selector is ready to accept new commands (i.e. it has finished the last operation) /// @returns true if the selector is ready to accept new commands (i.e. it has finished the last operation)
bool Step(); bool Step();
/// @returns the current slot of selector
/// 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 selector
inline uint8_t Slot() const { return currentSlot; }
/// @returns predefined positions of individual slots /// @returns predefined positions of individual slots
static constexpr mm::S_pos_t SlotPosition(uint8_t slot) { static constexpr mm::S_pos_t SlotPosition(uint8_t slot) {
return mm::unitToAxisUnit<mm::S_pos_t>(config::selectorSlotPositions[slot]); return mm::unitToAxisUnit<mm::S_pos_t>(config::selectorSlotPositions[slot]);
@ -50,13 +37,10 @@ public:
/// @returns the index of idle position of the selector, usually 5 in case of 0-4 valid indices of filament slots /// @returns the index of idle position of the selector, usually 5 in case of 0-4 valid indices of filament slots
inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; } inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; }
private: protected:
/// internal state of the automaton virtual void PrepareMoveToPlannedSlot() override;
uint8_t state;
uint8_t plannedSlot;
/// current state private:
uint8_t currentSlot;
}; };
/// The one and only instance of Selector in the FW /// The one and only instance of Selector in the FW

View File

@ -13,6 +13,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -13,6 +13,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -9,6 +9,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -9,6 +9,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -12,6 +12,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -96,11 +96,11 @@ void FailedLoadToFinda(uint8_t slot, logic::LoadFilament &lf) {
// Stage 2 - feeding to finda // Stage 2 - feeding to finda
// we'll assume the finda is defective here and does not trigger // we'll assume the finda is defective here and does not trigger
REQUIRE(WhileTopState(lf, ProgressCode::FeedingToFinda, 5000)); REQUIRE(WhileTopState(lf, ProgressCode::FeedingToFinda, 5000));
REQUIRE(VerifyState(lf, false, slot, slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERR1DisengagingIdler)); REQUIRE(VerifyState(lf, false, slot, slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERRDisengagingIdler));
// Stage 3 - disengaging idler in error mode // Stage 3 - disengaging idler in error mode
REQUIRE(WhileTopState(lf, ProgressCode::ERR1DisengagingIdler, 5000)); REQUIRE(WhileTopState(lf, ProgressCode::ERRDisengagingIdler, 5000));
REQUIRE(VerifyState(lf, false, mi::Idler::IdleSlotIndex(), slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERR1WaitingForUser)); REQUIRE(VerifyState(lf, false, mi::Idler::IdleSlotIndex(), slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERRWaitingForUser));
} }
void FailedLoadToFindaResolveHelp(uint8_t slot, logic::LoadFilament &lf) { void FailedLoadToFindaResolveHelp(uint8_t slot, logic::LoadFilament &lf) {
@ -119,12 +119,12 @@ void FailedLoadToFindaResolveHelp(uint8_t slot, logic::LoadFilament &lf) {
lf.Step(); lf.Step();
} }
REQUIRE(VerifyState(lf, false, mi::Idler::IdleSlotIndex(), slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERR1EngagingIdler)); REQUIRE(VerifyState(lf, false, mi::Idler::IdleSlotIndex(), slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERREngagingIdler));
// Stage 4 - engage the idler // Stage 4 - engage the idler
REQUIRE(WhileTopState(lf, ProgressCode::ERR1EngagingIdler, 5000)); REQUIRE(WhileTopState(lf, ProgressCode::ERREngagingIdler, 5000));
REQUIRE(VerifyState(lf, false, slot, slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERR1HelpingFilament)); REQUIRE(VerifyState(lf, false, slot, slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERRHelpingFilament));
} }
void FailedLoadToFindaResolveHelpFindaTriggered(uint8_t slot, logic::LoadFilament &lf) { void FailedLoadToFindaResolveHelpFindaTriggered(uint8_t slot, logic::LoadFilament &lf) {
@ -135,7 +135,7 @@ void FailedLoadToFindaResolveHelpFindaTriggered(uint8_t slot, logic::LoadFilamen
if(step == 100){ // on 100th step make FINDA trigger if(step == 100){ // on 100th step make FINDA trigger
hal::adc::SetADC(config::findaADCIndex, 1023); hal::adc::SetADC(config::findaADCIndex, 1023);
} }
return lf.TopLevelState() == ProgressCode::ERR1HelpingFilament; }, return lf.TopLevelState() == ProgressCode::ERRHelpingFilament; },
5000)); 5000));
REQUIRE(VerifyState(lf, false, slot, slot, true, ml::off, ml::blink0, ErrorCode::OK, ProgressCode::FeedingToBondtech)); REQUIRE(VerifyState(lf, false, slot, slot, true, ml::off, ml::blink0, ErrorCode::OK, ProgressCode::FeedingToBondtech));
@ -143,9 +143,9 @@ void FailedLoadToFindaResolveHelpFindaTriggered(uint8_t slot, logic::LoadFilamen
void FailedLoadToFindaResolveHelpFindaDidntTrigger(uint8_t slot, logic::LoadFilament &lf) { void FailedLoadToFindaResolveHelpFindaDidntTrigger(uint8_t slot, logic::LoadFilament &lf) {
// Stage 5 - move the pulley a bit - no FINDA change // Stage 5 - move the pulley a bit - no FINDA change
REQUIRE(WhileTopState(lf, ProgressCode::ERR1HelpingFilament, 5000)); REQUIRE(WhileTopState(lf, ProgressCode::ERRHelpingFilament, 5000));
REQUIRE(VerifyState(lf, false, slot, slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERR1DisengagingIdler)); REQUIRE(VerifyState(lf, false, slot, slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERRDisengagingIdler));
} }
TEST_CASE("load_filament::failed_load_to_finda_0-4_resolve_help_second_ok", "[load_filament]") { TEST_CASE("load_filament::failed_load_to_finda_0-4_resolve_help_second_ok", "[load_filament]") {

View File

@ -15,6 +15,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -12,6 +12,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp

View File

@ -158,10 +158,10 @@ void FindaDidntTriggerCommonSetup(uint8_t slot, logic::UnloadFilament &uf) {
// no change in selector's position // no change in selector's position
// FINDA still on // FINDA still on
// red LED should blink, green LED should be off // red LED should blink, green LED should be off
REQUIRE(VerifyState(uf, true, slot, slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERR1DisengagingIdler)); REQUIRE(VerifyState(uf, true, slot, slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERRDisengagingIdler));
// Stage 2 - idler should get disengaged // Stage 2 - idler should get disengaged
REQUIRE(WhileTopState(uf, ProgressCode::ERR1DisengagingIdler, 5000)); REQUIRE(WhileTopState(uf, ProgressCode::ERRDisengagingIdler, 5000));
// we still think we have filament loaded at this stage // we still think we have filament loaded at this stage
// idler should have been disengaged // idler should have been disengaged
@ -169,7 +169,7 @@ void FindaDidntTriggerCommonSetup(uint8_t slot, logic::UnloadFilament &uf) {
// FINDA still on // FINDA still on
// red LED should blink // red LED should blink
// green LED should be off // green LED should be off
REQUIRE(VerifyState(uf, true, mi::Idler::IdleSlotIndex(), slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERR1WaitingForUser)); REQUIRE(VerifyState(uf, true, mi::Idler::IdleSlotIndex(), slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERRWaitingForUser));
} }
void FindaDidntTriggerResolveHelp(uint8_t slot, logic::UnloadFilament &uf) { void FindaDidntTriggerResolveHelp(uint8_t slot, logic::UnloadFilament &uf) {
@ -194,17 +194,17 @@ void FindaDidntTriggerResolveHelp(uint8_t slot, logic::UnloadFilament &uf) {
// no change in selector's position // no change in selector's position
// FINDA still on // FINDA still on
// red LED should blink, green LED should be off // red LED should blink, green LED should be off
REQUIRE(VerifyState(uf, true, mi::Idler::IdleSlotIndex(), slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERR1EngagingIdler)); REQUIRE(VerifyState(uf, true, mi::Idler::IdleSlotIndex(), slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERREngagingIdler));
// Stage 4 - engage the idler // Stage 4 - engage the idler
REQUIRE(WhileTopState(uf, ProgressCode::ERR1EngagingIdler, 5000)); REQUIRE(WhileTopState(uf, ProgressCode::ERREngagingIdler, 5000));
// we still think we have filament loaded at this stage // we still think we have filament loaded at this stage
// idler should be engaged // idler should be engaged
// no change in selector's position // no change in selector's position
// FINDA still on // FINDA still on
// red LED should blink, green LED should be off // red LED should blink, green LED should be off
REQUIRE(VerifyState(uf, true, slot, slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERR1HelpingFilament)); REQUIRE(VerifyState(uf, true, slot, slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERRHelpingFilament));
} }
void FindaDidntTriggerResolveHelpFindaTriggered(uint8_t slot, logic::UnloadFilament &uf) { void FindaDidntTriggerResolveHelpFindaTriggered(uint8_t slot, logic::UnloadFilament &uf) {
@ -215,7 +215,7 @@ void FindaDidntTriggerResolveHelpFindaTriggered(uint8_t slot, logic::UnloadFilam
if(step == 100){ // on 100th step make FINDA trigger if(step == 100){ // on 100th step make FINDA trigger
hal::adc::SetADC(config::findaADCIndex, 0); hal::adc::SetADC(config::findaADCIndex, 0);
} }
return uf.TopLevelState() == ProgressCode::ERR1HelpingFilament; }, return uf.TopLevelState() == ProgressCode::ERRHelpingFilament; },
5000)); 5000));
// we still think we have filament loaded at this stage // we still think we have filament loaded at this stage
@ -228,14 +228,14 @@ void FindaDidntTriggerResolveHelpFindaTriggered(uint8_t slot, logic::UnloadFilam
void FindaDidntTriggerResolveHelpFindaDidntTrigger(uint8_t slot, logic::UnloadFilament &uf) { void FindaDidntTriggerResolveHelpFindaDidntTrigger(uint8_t slot, logic::UnloadFilament &uf) {
// Stage 5 - move the pulley a bit - no FINDA change // Stage 5 - move the pulley a bit - no FINDA change
REQUIRE(WhileTopState(uf, ProgressCode::ERR1HelpingFilament, 5000)); REQUIRE(WhileTopState(uf, ProgressCode::ERRHelpingFilament, 5000));
// we still think we have filament loaded at this stage // we still think we have filament loaded at this stage
// idler should be engaged // idler should be engaged
// no change in selector's position // no change in selector's position
// FINDA still pressed // FINDA still pressed
// red LED should blink, green LED should be off // red LED should blink, green LED should be off
REQUIRE(VerifyState(uf, true, slot, slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERR1DisengagingIdler)); REQUIRE(VerifyState(uf, true, slot, slot, true, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_OFF, ProgressCode::ERRDisengagingIdler));
} }
TEST_CASE("unload_filament::finda_didnt_trigger_resolve_help_second_ok", "[unload_filament]") { TEST_CASE("unload_filament::finda_didnt_trigger_resolve_help_second_ok", "[unload_filament]") {

View File

@ -9,6 +9,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp ${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.cpp ${CMAKE_SOURCE_DIR}/src/modules/idler.cpp
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/movable_base.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp ${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${CMAKE_SOURCE_DIR}/src/modules/selector.cpp ${CMAKE_SOURCE_DIR}/src/modules/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp ${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp