From 10a9e0f0a07a85587897db1793ba00db6dbebfcd Mon Sep 17 00:00:00 2001 From: "D.R.racer" Date: Thu, 17 Nov 2022 10:38:14 +0100 Subject: [PATCH] Recover from MMU restart while performing a command PFW-1427 --- Firmware/mmu2.cpp | 70 ++++++++++++++++++++++++-------- Firmware/mmu2.h | 3 +- Firmware/mmu2_protocol_logic.cpp | 15 ++++--- Firmware/mmu2_protocol_logic.h | 3 +- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/Firmware/mmu2.cpp b/Firmware/mmu2.cpp index 2f1484ea9..4965a71b0 100644 --- a/Firmware/mmu2.cpp +++ b/Firmware/mmu2.cpp @@ -193,8 +193,10 @@ void MMU2::PowerOn(){ bool MMU2::ReadRegister(uint8_t address){ if( ! WaitForMMUReady()) return false; - logic.ReadRegister(address); // we may signal the accepted/rejected status of the response as return value of this function - manage_response(false, false); + do { + logic.ReadRegister(address); // we may signal the accepted/rejected status of the response as return value of this function + } while( ! manage_response(false, false) ); + return true; } @@ -207,8 +209,10 @@ bool MMU2::WriteRegister(uint8_t address, uint16_t data){ logic.PlanExtraLoadDistance(data); } - logic.WriteRegister(address, data); // we may signal the accepted/rejected status of the response as return value of this function - manage_response(false, false); + do { + logic.WriteRegister(address, data); // we may signal the accepted/rejected status of the response as return value of this function + } while( ! manage_response(false, false) ); + return true; } @@ -333,8 +337,10 @@ bool MMU2::tool_change(uint8_t index) { tool_change_extruder = index; logic.ToolChange(index); // let the MMU pull the filament out and push a new one in - manage_response(true, true); - + if( ! manage_response(true, true) ){ + // @@TODO failed to perform the command - retry + ; + } // reset current position to whatever the planner thinks it is plan_set_e_position(current_position[E_AXIS]); @@ -371,7 +377,10 @@ bool MMU2::tool_change(char code, uint8_t slot) { st_synchronize(); tool_change_extruder = slot; logic.ToolChange(slot); - manage_response(false, false); + if( ! manage_response(false, false) ){ + // @@TODO failed to perform the command - retry + ; + } extruder = slot; SpoolJoin::spooljoin.setSlot(slot); set_extrude_min_temp(EXTRUDE_MINTEMP); @@ -409,7 +418,10 @@ bool MMU2::set_filament_type(uint8_t index, uint8_t type) { // cmd_arg = filamentType; // command(MMU_CMD_F0 + index); - manage_response(false, false); // true, true); -- Comment: how is it possible for a filament type set to fail? + if( ! manage_response(false, false) ){ + // @@TODO failed to perform the command - retry + ; + } // true, true); -- Comment: how is it possible for a filament type set to fail? return true; } @@ -426,7 +438,10 @@ bool MMU2::unload() { filament_ramming(); logic.UnloadFilament(); - manage_response(false, true); + if( ! manage_response(false, true) ){ + // @@TODO failed to perform the command - retry + ; + } Sound_MakeSound(e_SOUND_TYPE_StandardConfirm); // no active tool @@ -442,7 +457,10 @@ bool MMU2::cut_filament(uint8_t index){ ReportingRAII rep(CommandInProgress::CutFilament); logic.CutFilament(index); - manage_response(false, true); + if( ! manage_response(false, true) ){ + // @@TODO failed to perform the command - retry + ; + } return true; } @@ -472,7 +490,10 @@ bool MMU2::load_filament(uint8_t index) { ReportingRAII rep(CommandInProgress::LoadFilament); logic.LoadFilament(index); - manage_response(false, false); + if( ! manage_response(false, false) ){ + // @@TODO failed to perform the command - retry + ; + } Sound_MakeSound(e_SOUND_TYPE_StandardConfirm); lcd_update_enable(true); @@ -510,7 +531,10 @@ bool MMU2::load_filament_to_nozzle(uint8_t index) { tool_change_extruder = index; logic.ToolChange(index); - manage_response(true, true); + if( ! manage_response(true, true) ){ + // @@TODO failed to perform the command - retry + ; + } // The MMU's idler is disengaged at this point // That means the MK3/S now has fully control @@ -541,7 +565,10 @@ bool MMU2::eject_filament(uint8_t index, bool recover) { plan_buffer_line_curposXYZE(2500.F / 60.F); st_synchronize(); logic.EjectFilament(index); - manage_response(false, false); + if( ! manage_response(false, false) ){ + // @@TODO failed to perform the command - retry + ; + } if (recover) { // LCD_MESSAGEPGM(MSG_MMU2_EJECT_RECOVER); @@ -559,7 +586,10 @@ bool MMU2::eject_filament(uint8_t index, bool recover) { Sound_MakeSound(e_SOUND_TYPE_StandardConfirm); // logic.Command(); //@@TODO command(MMU_CMD_R0); - manage_response(false, false); + if( ! manage_response(false, false) ){ + // @@TODO failed to perform the command - retry + ; + } } // no active tool @@ -702,7 +732,7 @@ void MMU2::CheckUserInput(){ /// It is closely related to mmu_loop() (which corresponds to our ProtocolLogic::Step()), which does NOT perform any blocking wait for a command to finish. /// But - in case of an error, the command is not yet finished, but we must react accordingly - move the printhead elsewhere, stop heating, eat a cat or so. /// That's what's being done here... -void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { +bool MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { mmu_print_saved = SavedState::None; KEEPALIVE_STATE(IN_PROCESS); @@ -741,10 +771,13 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { ResumeUnpark(); // We can now travel back to the tower or wherever we were when we saved. ResetRetryAttempts(); // Reset the retry counter. st_synchronize(); - return; + return true; + case Interrupted: + // now what :D ... big bad ... ramming, unload, retry the whole command originally issued + return false; case VersionMismatch: // this basically means the MMU will be disabled until reconnected CheckUserInput(); - return; + return true; case CommandError: // Don't proceed to the park/save if we are doing an autoretry. if (inAutoRetry){ @@ -784,6 +817,9 @@ StepStatus MMU2::LogicStep(bool reportErrors) { LogEchoEvent_P(PSTR("MMU Button pushed")); CheckUserInput(); // Process the button immediately break; + case Interrupted: + // can be silently handed over to a higher layer, no processing necessary at this spot + break; default: if(reportErrors) { switch (ss) diff --git a/Firmware/mmu2.h b/Firmware/mmu2.h index a208057f9..e52f7db5e 100644 --- a/Firmware/mmu2.h +++ b/Firmware/mmu2.h @@ -227,7 +227,8 @@ private: /// Along with the mmu_loop method, this loops until a response from the MMU is received and acts upon. /// In case of an error, it parks the print head and turns off nozzle heating - void manage_response(const bool move_axes, const bool turn_off_nozzle); + /// @returns false if the command could not have been completed (MMU interrupted) + [[nodiscard]] bool manage_response(const bool move_axes, const bool turn_off_nozzle); /// The inner private implementation of mmu_loop() /// which is NOT (!!!) recursion-guarded. Use caution - but we do need it during waiting for hotend resume to keep comms alive! diff --git a/Firmware/mmu2_protocol_logic.cpp b/Firmware/mmu2_protocol_logic.cpp index a9070eb76..f3720bf45 100644 --- a/Firmware/mmu2_protocol_logic.cpp +++ b/Firmware/mmu2_protocol_logic.cpp @@ -295,11 +295,9 @@ StepStatus ProtocolLogic::StartSeqStep() { return Processing; // Returning Finished is not a good idea in case of a fast error recovery // - it tells the printer, that the command which experienced a protocol error and recovered successfully actually terminated. // In such a case we must return "Processing" in order to keep the MMU state machine running and prevent the printer from executing next G-codes. - break; default: return VersionMismatch; } - return Finished; } StepStatus ProtocolLogic::DelayedRestartWait() { @@ -344,9 +342,16 @@ StepStatus ProtocolLogic::ProcessCommandQueryResponse() { SendAndUpdateFilamentSensor(); return ButtonPushed; case ResponseMsgParamCodes::Finished: - progressCode = ProgressCode::OK; - scopeState = ScopeState::Ready; - return Finished; + // We must check whether the "finished" is actually related to the command issued into the MMU + // It can also be an X0 F which means MMU just successfully restarted. + if( ReqMsg().code == rsp.request.code && ReqMsg().value == rsp.request.value ){ + progressCode = ProgressCode::OK; + scopeState = ScopeState::Ready; + return Finished; + } else { + // got response to some other command - the originally issued command was interrupted! + return Interrupted; + } default: return ProtocolError; } diff --git a/Firmware/mmu2_protocol_logic.h b/Firmware/mmu2_protocol_logic.h index 35000c031..f80310800 100644 --- a/Firmware/mmu2_protocol_logic.h +++ b/Firmware/mmu2_protocol_logic.h @@ -33,7 +33,8 @@ class ProtocolLogic; enum StepStatus : uint_fast8_t { Processing = 0, MessageReady, ///< a message has been successfully decoded from the received bytes - Finished, + Finished, ///< Scope finished successfully + Interrupted, ///< received "Finished" message related to a different command than originally issued (most likely the MMU restarted while doing something) CommunicationTimeout, ///< the MMU failed to respond to a request within a specified time frame ProtocolError, ///< bytes read from the MMU didn't form a valid response CommandRejected, ///< the MMU rejected the command due to some other command in progress, may be the user is operating the MMU locally (button commands)