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

View File

@ -1,25 +1,54 @@
#pragma once
#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,
/// 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
/// therefore the error codes have been extracted to one place.
///
/// Please note the errors are intentionally coded as "negative" values (highest bit set),
/// 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
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_OFF = -2, ///< FINDA didn't switch off while unloading filament
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 = 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_OFF = -4, ///< Filament sensor didn't switch off while performing UnloadFilament
FSENSOR_DIDNT_SWITCH_ON = 0x8003, ///< Filament sensor didn't switch on while performing LoadFilament
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.State() == FeedToFinda::Failed) {
// @@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;
mi::idler.Disengage();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::Color::green, ml::Mode::off);
@ -74,18 +74,18 @@ bool LoadFilament::StepInner() {
break;
case ProgressCode::OK:
return true;
case ProgressCode::ERR1DisengagingIdler: // couldn't unload to FINDA
case ProgressCode::ERRDisengagingIdler: // couldn't unload to FINDA
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
}
return false;
case ProgressCode::ERR1WaitingForUser: {
case ProgressCode::ERRWaitingForUser: {
// waiting for user buttons and/or a command from the printer
mu::Event ev = mu::userInput.ConsumeEvent();
switch (ev) {
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());
break;
case mu::Event::Middle: // try again the whole sequence
@ -102,20 +102,20 @@ bool LoadFilament::StepInner() {
}
return false;
}
case ProgressCode::ERR1EngagingIdler:
case ProgressCode::ERREngagingIdler:
if (mi::idler.Engaged()) {
state = ProgressCode::ERR1HelpingFilament;
state = ProgressCode::ERRHelpingFilament;
mm::motion.PlanMove(mm::Pulley, 450, 5000); //@@TODO constants
}
return false;
case ProgressCode::ERR1HelpingFilament:
case ProgressCode::ERRHelpingFilament:
if (mf::finda.Pressed()) {
// the help was enough to press the FINDA, we are ok, continue normally
state = ProgressCode::FeedingToBondtech;
error = ErrorCode::OK;
} else if (mm::motion.QueueEmpty()) {
// helped a bit, but FINDA didn't trigger, return to the main error state
state = ProgressCode::ERR1DisengagingIdler;
state = ProgressCode::ERRDisengagingIdler;
}
return false;
default: // we got into an unhandled state, better report it

View File

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

View File

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

View File

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

View File

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

View File

@ -11,57 +11,34 @@ Idler idler;
namespace mm = modules::motion;
Idler::EngageDisengage Idler::Disengage() {
if (state == Moving)
return EngageDisengage::Refused;
void Idler::PrepareMoveToPlannedSlot() {
mm::motion.PlanMoveTo<mm::Idler>(SlotPosition(plannedSlot), 1000._I_deg_s); // @@TODO
}
Idler::OperationResult Idler::Disengage() {
if (state == Moving)
return OperationResult::Refused;
plannedSlot = IdleSlotIndex();
plannedEngage = false;
if (!Engaged())
return true;
return OperationResult::Accepted;
mm::motion.InitAxis(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;
// }
return InitMovement(mm::Idler);
}
Idler::EngageDisengage Idler::Engage(uint8_t slot) {
Idler::OperationResult Idler::Engage(uint8_t slot) {
if (state == Moving)
return EngageDisengage::Refused;
return OperationResult::Refused;
plannedSlot = slot;
plannedEngage = true;
if (Engaged())
return true;
return OperationResult::Accepted;
mm::motion.InitAxis(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;
// }
return InitMovement(mm::Idler);
}
bool Idler::Home() {

View File

@ -1,6 +1,7 @@
#pragma once
#include "../config/config.h"
#include "../modules/axisunit.h"
#include "axisunit.h"
#include "movable_base.h"
namespace modules {
@ -10,37 +11,21 @@ namespace idler {
namespace mm = modules::motion;
/// The Idler model handles asynchronnous Engaging / Disengaging operations and keeps track of idler's current state.
class Idler {
class Idler : public motion::MovableBase {
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()
: state(Ready)
: MovableBase()
, plannedEngage(false)
, plannedSlot(0)
, currentSlot(0)
, 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
/// @param slot index to be activated
/// @returns #EngageDisengage
EngageDisengage Engage(uint8_t slot);
OperationResult Engage(uint8_t slot);
/// Plan disengaging of the idler, i.e. parking the idler
/// @returns #EngageDisengage
EngageDisengage Disengage();
OperationResult Disengage();
/// Plan homing of the idler axis
/// @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
inline bool Engaged() const { return currentlyEngaged; }
/// @returns currently active slot
inline uint8_t Slot() const { return currentSlot; }
/// @returns predefined positions of individual slots
static constexpr mm::I_pos_t SlotPosition(uint8_t 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
inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; }
/// @returns internal state of the Idler
inline uint8_t State() const { return state; }
protected:
virtual void PrepareMoveToPlannedSlot() override;
private:
/// internal state of the automaton
uint8_t state;
/// direction of travel - engage/disengage
bool plannedEngage;
uint8_t plannedSlot;
/// current state
uint8_t currentSlot;
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;
}
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[1] = msg.value + '0';
txbuff[2] = ' ';
@ -209,10 +209,21 @@ uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMs
} else if (value < 100) {
*dst++ = value / 10 + '0';
*dst++ = value % 10 + '0';
} else {
} else if (value < 1000) {
*dst++ = value / 100 + '0';
*dst++ = (value / 10) % 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';

View File

@ -52,12 +52,12 @@ struct RequestMsg {
struct ResponseMsg {
RequestMsg request; ///< response is always preceeded by the request message
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 paramCode code 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)
, paramCode(paramCode)
, paramValue(paramValue) {}
@ -123,7 +123,7 @@ public:
/// @param value related to status of operation(e.g. error code or progress)
/// @param txbuff where to format the message
/// @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
inline const RequestMsg GetRequestMsg() const { return requestMsg; }

View File

@ -11,19 +11,20 @@ Selector selector;
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)
return false;
return OperationResult::Refused;
plannedSlot = slot;
if (currentSlot == slot)
return true;
return OperationResult::Accepted;
mm::motion.InitAxis(mm::Selector);
mm::motion.PlanMoveTo<mm::Selector>(SlotPosition(slot), 1000.0_S_mm_s); // @@TODO
state = Moving;
return true;
return InitMovement(mm::Selector);
}
bool Selector::Home() {

View File

@ -1,6 +1,7 @@
#pragma once
#include "../config/config.h"
#include "../modules/axisunit.h"
#include "axisunit.h"
#include "movable_base.h"
namespace modules {
@ -10,24 +11,15 @@ namespace selector {
namespace mm = modules::motion;
/// 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:
/// Internal states of selector's state machine
enum {
Ready = 0,
Moving,
Failed
};
inline constexpr Selector()
: state(Ready)
, plannedSlot(0)
, currentSlot(0) {}
: MovableBase() {}
/// Plan move of the selector to a specific filament slot
/// @param slot index to move to
/// @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
/// @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)
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
static constexpr mm::S_pos_t SlotPosition(uint8_t 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
inline static constexpr uint8_t IdleSlotIndex() { return config::toolCount; }
private:
/// internal state of the automaton
uint8_t state;
uint8_t plannedSlot;
protected:
virtual void PrepareMoveToPlannedSlot() override;
/// current state
uint8_t currentSlot;
private:
};
/// 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/idler.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/selector.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/idler.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/selector.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/idler.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/selector.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/idler.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/selector.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/idler.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/selector.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
// we'll assume the finda is defective here and does not trigger
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
REQUIRE(WhileTopState(lf, ProgressCode::ERR1DisengagingIdler, 5000));
REQUIRE(VerifyState(lf, false, mi::Idler::IdleSlotIndex(), slot, false, ml::off, ml::blink0, ErrorCode::FINDA_DIDNT_SWITCH_ON, ProgressCode::ERR1WaitingForUser));
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::ERRWaitingForUser));
}
void FailedLoadToFindaResolveHelp(uint8_t slot, logic::LoadFilament &lf) {
@ -119,12 +119,12 @@ void FailedLoadToFindaResolveHelp(uint8_t slot, logic::LoadFilament &lf) {
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
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) {
@ -135,7 +135,7 @@ void FailedLoadToFindaResolveHelpFindaTriggered(uint8_t slot, logic::LoadFilamen
if(step == 100){ // on 100th step make FINDA trigger
hal::adc::SetADC(config::findaADCIndex, 1023);
}
return lf.TopLevelState() == ProgressCode::ERR1HelpingFilament; },
return lf.TopLevelState() == ProgressCode::ERRHelpingFilament; },
5000));
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) {
// 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]") {

View File

@ -15,6 +15,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.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/selector.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/idler.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/selector.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
// FINDA still on
// 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
REQUIRE(WhileTopState(uf, ProgressCode::ERR1DisengagingIdler, 5000));
REQUIRE(WhileTopState(uf, ProgressCode::ERRDisengagingIdler, 5000));
// we still think we have filament loaded at this stage
// idler should have been disengaged
@ -169,7 +169,7 @@ void FindaDidntTriggerCommonSetup(uint8_t slot, logic::UnloadFilament &uf) {
// FINDA still on
// 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::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) {
@ -194,17 +194,17 @@ void FindaDidntTriggerResolveHelp(uint8_t slot, logic::UnloadFilament &uf) {
// no change in selector's position
// FINDA still on
// 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
REQUIRE(WhileTopState(uf, ProgressCode::ERR1EngagingIdler, 5000));
REQUIRE(WhileTopState(uf, ProgressCode::ERREngagingIdler, 5000));
// we still think we have filament loaded at this stage
// idler should be engaged
// no change in selector's position
// FINDA still on
// 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) {
@ -215,7 +215,7 @@ void FindaDidntTriggerResolveHelpFindaTriggered(uint8_t slot, logic::UnloadFilam
if(step == 100){ // on 100th step make FINDA trigger
hal::adc::SetADC(config::findaADCIndex, 0);
}
return uf.TopLevelState() == ProgressCode::ERR1HelpingFilament; },
return uf.TopLevelState() == ProgressCode::ERRHelpingFilament; },
5000));
// 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) {
// 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
// idler should be engaged
// no change in selector's position
// FINDA still pressed
// 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]") {

View File

@ -9,6 +9,7 @@ add_executable(
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/idler.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/selector.cpp
${CMAKE_SOURCE_DIR}/src/modules/user_input.cpp