diff --git a/src/logic/command_base.cpp b/src/logic/command_base.cpp index 2ca28e9..3106ac4 100644 --- a/src/logic/command_base.cpp +++ b/src/logic/command_base.cpp @@ -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 diff --git a/src/logic/error_codes.h b/src/logic/error_codes.h index c7978c1..00bb6c9 100644 --- a/src/logic/error_codes.h +++ b/src/logic/error_codes.h @@ -1,25 +1,54 @@ #pragma once #include -/// 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 }; diff --git a/src/logic/load_filament.cpp b/src/logic/load_filament.cpp index 1467c3a..3089a0f 100644 --- a/src/logic/load_filament.cpp +++ b/src/logic/load_filament.cpp @@ -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 diff --git a/src/logic/progress_codes.h b/src/logic/progress_codes.h index b0d1261..31a5f2b 100644 --- a/src/logic/progress_codes.h +++ b/src/logic/progress_codes.h @@ -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, diff --git a/src/logic/unload_filament.cpp b/src/logic/unload_filament.cpp index a2472bb..57b4fff 100644 --- a/src/logic/unload_filament.cpp +++ b/src/logic/unload_filament.cpp @@ -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: diff --git a/src/main.cpp b/src/main.cpp index 38ff65f..aa8e2eb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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; } diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index c653a1a..3c5856c 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -9,6 +9,7 @@ target_sources( idler.cpp leds.cpp motion.cpp + movable_base.cpp permanent_storage.cpp selector.cpp timebase.cpp diff --git a/src/modules/idler.cpp b/src/modules/idler.cpp index 66b057e..9d7193d 100644 --- a/src/modules/idler.cpp +++ b/src/modules/idler.cpp @@ -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(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(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(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() { diff --git a/src/modules/idler.h b/src/modules/idler.h index 78fe803..58b6042 100644 --- a/src/modules/idler.h +++ b/src/modules/idler.h @@ -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(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; }; diff --git a/src/modules/movable_base.cpp b/src/modules/movable_base.cpp new file mode 100644 index 0000000..8450827 --- /dev/null +++ b/src/modules/movable_base.cpp @@ -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 diff --git a/src/modules/movable_base.h b/src/modules/movable_base.h new file mode 100644 index 0000000..12033ea --- /dev/null +++ b/src/modules/movable_base.h @@ -0,0 +1,56 @@ +#pragma once +#include +#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 diff --git a/src/modules/protocol.cpp b/src/modules/protocol.cpp index 2b60404..3acaf34 100644 --- a/src/modules/protocol.cpp +++ b/src/modules/protocol.cpp @@ -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'; diff --git a/src/modules/protocol.h b/src/modules/protocol.h index dea8ea1..877fedb 100644 --- a/src/modules/protocol.h +++ b/src/modules/protocol.h @@ -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; } diff --git a/src/modules/selector.cpp b/src/modules/selector.cpp index 6ad25b1..ab2dc84 100644 --- a/src/modules/selector.cpp +++ b/src/modules/selector.cpp @@ -11,19 +11,20 @@ Selector selector; namespace mm = modules::motion; -bool Selector::MoveToSlot(uint8_t slot) { +void Selector::PrepareMoveToPlannedSlot() { + mm::motion.PlanMoveTo(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(SlotPosition(slot), 1000.0_S_mm_s); // @@TODO - state = Moving; - return true; + return InitMovement(mm::Selector); } bool Selector::Home() { diff --git a/src/modules/selector.h b/src/modules/selector.h index 8f6b347..ca867af 100644 --- a/src/modules/selector.h +++ b/src/modules/selector.h @@ -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(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 diff --git a/tests/unit/logic/cut_filament/CMakeLists.txt b/tests/unit/logic/cut_filament/CMakeLists.txt index 7a68f82..b3b46d7 100644 --- a/tests/unit/logic/cut_filament/CMakeLists.txt +++ b/tests/unit/logic/cut_filament/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/eject_filament/CMakeLists.txt b/tests/unit/logic/eject_filament/CMakeLists.txt index eab363d..c105a92 100644 --- a/tests/unit/logic/eject_filament/CMakeLists.txt +++ b/tests/unit/logic/eject_filament/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/feed_to_bondtech/CMakeLists.txt b/tests/unit/logic/feed_to_bondtech/CMakeLists.txt index 66e312e..7558c11 100644 --- a/tests/unit/logic/feed_to_bondtech/CMakeLists.txt +++ b/tests/unit/logic/feed_to_bondtech/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/feed_to_finda/CMakeLists.txt b/tests/unit/logic/feed_to_finda/CMakeLists.txt index bd3466c..0f93766 100644 --- a/tests/unit/logic/feed_to_finda/CMakeLists.txt +++ b/tests/unit/logic/feed_to_finda/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/load_filament/CMakeLists.txt b/tests/unit/logic/load_filament/CMakeLists.txt index e8f2fb9..c435fa9 100644 --- a/tests/unit/logic/load_filament/CMakeLists.txt +++ b/tests/unit/logic/load_filament/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/load_filament/test_load_filament.cpp b/tests/unit/logic/load_filament/test_load_filament.cpp index 08091d5..a8fe2cc 100644 --- a/tests/unit/logic/load_filament/test_load_filament.cpp +++ b/tests/unit/logic/load_filament/test_load_filament.cpp @@ -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]") { diff --git a/tests/unit/logic/tool_change/CMakeLists.txt b/tests/unit/logic/tool_change/CMakeLists.txt index 1b38c07..d965ec7 100644 --- a/tests/unit/logic/tool_change/CMakeLists.txt +++ b/tests/unit/logic/tool_change/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/unload_filament/CMakeLists.txt b/tests/unit/logic/unload_filament/CMakeLists.txt index b3d0672..7f4b1da 100644 --- a/tests/unit/logic/unload_filament/CMakeLists.txt +++ b/tests/unit/logic/unload_filament/CMakeLists.txt @@ -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 diff --git a/tests/unit/logic/unload_filament/test_unload_filament.cpp b/tests/unit/logic/unload_filament/test_unload_filament.cpp index f81aaa8..5d02745 100644 --- a/tests/unit/logic/unload_filament/test_unload_filament.cpp +++ b/tests/unit/logic/unload_filament/test_unload_filament.cpp @@ -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]") { diff --git a/tests/unit/logic/unload_to_finda/CMakeLists.txt b/tests/unit/logic/unload_to_finda/CMakeLists.txt index 33c2f87..33ace3b 100644 --- a/tests/unit/logic/unload_to_finda/CMakeLists.txt +++ b/tests/unit/logic/unload_to_finda/CMakeLists.txt @@ -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