mmu2_protocol_logic.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #pragma once
  2. #include <stdint.h>
  3. // #include <array> //@@TODO Don't we have STL for AVR somewhere?
  4. template<typename T, uint8_t N>
  5. class array {
  6. T data[N];
  7. public:
  8. array() = default;
  9. inline constexpr T* begin()const { return data; }
  10. inline constexpr T* end()const { return data + N; }
  11. constexpr uint8_t size()const { return N; }
  12. inline T &operator[](uint8_t i){
  13. return data[i];
  14. }
  15. };
  16. #include "mmu2/error_codes.h"
  17. #include "mmu2/progress_codes.h"
  18. #include "mmu2/buttons.h"
  19. #include "mmu2_protocol.h"
  20. #include "mmu2_serial.h"
  21. /// New MMU2 protocol logic
  22. namespace MMU2 {
  23. using namespace modules::protocol;
  24. class ProtocolLogic;
  25. /// ProtocolLogic stepping statuses
  26. enum StepStatus : uint_fast8_t {
  27. Processing = 0,
  28. MessageReady, ///< a message has been successfully decoded from the received bytes
  29. Finished,
  30. CommunicationTimeout, ///< the MMU failed to respond to a request within a specified time frame
  31. ProtocolError, ///< bytes read from the MMU didn't form a valid response
  32. CommandRejected, ///< the MMU rejected the command due to some other command in progress, may be the user is operating the MMU locally (button commands)
  33. CommandError, ///< the command in progress stopped due to unrecoverable error, user interaction required
  34. VersionMismatch, ///< the MMU reports its firmware version incompatible with our implementation
  35. CommunicationRecovered,
  36. ButtonPushed, ///< The MMU reported the user pushed one of its three buttons.
  37. };
  38. static constexpr uint32_t linkLayerTimeout = 2000; ///< default link layer communication timeout
  39. static constexpr uint32_t dataLayerTimeout = linkLayerTimeout * 3; ///< data layer communication timeout
  40. static constexpr uint32_t heartBeatPeriod = linkLayerTimeout / 2; ///< period of heart beat messages (Q0)
  41. static_assert( heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts");
  42. /// Base class for sub-automata of the ProtocolLogic class.
  43. /// Their operation should never block (wait inside).
  44. class ProtocolLogicPartBase {
  45. public:
  46. inline ProtocolLogicPartBase(ProtocolLogic *logic)
  47. : logic(logic)
  48. , state(State::Ready) {}
  49. /// Restarts the sub-automaton
  50. virtual void Restart() = 0;
  51. /// Makes one step in the sub-automaton
  52. /// @returns StepStatus
  53. virtual StepStatus Step() = 0;
  54. /// @returns true if the state machine is waiting for a response from the MMU
  55. bool ExpectsResponse()const { return state != State::Ready && state != State::Wait; }
  56. protected:
  57. ProtocolLogic *logic; ///< pointer to parent ProtocolLogic layer
  58. friend class ProtocolLogic;
  59. /// Common internal states of the derived sub-automata
  60. /// General rule of thumb: *Sent states are waiting for a response from the MMU
  61. enum class State : uint_fast8_t {
  62. Ready,
  63. Wait,
  64. S0Sent, // beware - due to optimization reasons these SxSent must be kept one after another
  65. S1Sent,
  66. S2Sent,
  67. QuerySent,
  68. CommandSent,
  69. FilamentSensorStateSent,
  70. FINDAReqSent,
  71. ButtonSent,
  72. ContinueFromIdle,
  73. RecoveringProtocolError
  74. };
  75. State state; ///< internal state of the sub-automaton
  76. /// @returns the status of processing of the FINDA query response
  77. /// @param finishedRV returned value in case the message was successfully received and processed
  78. /// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
  79. StepStatus ProcessFINDAReqSent(StepStatus finishedRV, State nextState);
  80. /// Called repeatedly while waiting for a query (Q0) period.
  81. /// All event checks to report immediately from the printer to the MMU shall be done in this method.
  82. /// So far, the only such a case is the filament sensor, but there can be more like this in the future.
  83. void CheckAndReportAsyncEvents();
  84. void SendQuery();
  85. void SendFINDAQuery();
  86. void SendAndUpdateFilamentSensor();
  87. void SendButton(uint8_t btn);
  88. void SendVersion(uint8_t stage);
  89. };
  90. /// Starting sequence of the communication with the MMU.
  91. /// The printer shall ask for MMU's version numbers.
  92. /// If everything goes well and the MMU's version is good enough,
  93. /// the ProtocolLogic layer may continue talking to the MMU
  94. class StartSeq : public ProtocolLogicPartBase {
  95. public:
  96. inline StartSeq(ProtocolLogic *logic)
  97. : ProtocolLogicPartBase(logic)
  98. , retries(maxRetries) {}
  99. void Restart() override;
  100. StepStatus Step() override;
  101. private:
  102. static constexpr uint8_t maxRetries = 6;
  103. uint8_t retries;
  104. };
  105. class DelayedRestart : public ProtocolLogicPartBase {
  106. public:
  107. inline DelayedRestart(ProtocolLogic *logic)
  108. : ProtocolLogicPartBase(logic) {}
  109. void Restart() override;
  110. StepStatus Step() override;
  111. };
  112. /// A command and its lifecycle.
  113. /// CommandSent:
  114. /// - the command was placed into the UART TX buffer, awaiting response from the MMU
  115. /// - if the MMU confirms the command, we'll wait for it to finish
  116. /// - if the MMU refuses the command, we report an error (should normally not happen unless someone is hacking the communication without waiting for the previous command to finish)
  117. /// Wait:
  118. /// - waiting for the MMU to process the command - may take several seconds, for example Tool change operation
  119. /// - meawhile, every 300ms we send a Q0 query to obtain the current state of the command being processed
  120. /// - as soon as we receive a response to Q0 from the MMU, we process it in the next state
  121. /// QuerySent - check the reply from the MMU - can be any of the following:
  122. /// - Processing: the MMU is still working
  123. /// - Error: the command failed on the MMU, we'll have the exact error report in the response message
  124. /// - Finished: the MMU finished the command successfully, another command may be issued now
  125. class Command : public ProtocolLogicPartBase {
  126. public:
  127. inline Command(ProtocolLogic *logic)
  128. : ProtocolLogicPartBase(logic)
  129. , rq(RequestMsgCodes::unknown, 0) {}
  130. void Restart() override;
  131. StepStatus Step() override;
  132. inline void SetRequestMsg(RequestMsg msg) {
  133. rq = msg;
  134. }
  135. void ContinueFromIdle(){
  136. state = State::ContinueFromIdle;
  137. }
  138. inline const RequestMsg &ReqMsg()const { return rq; }
  139. private:
  140. RequestMsg rq;
  141. };
  142. /// Idle state - we have no command for the MMU, so we are only regularly querying its state with Q0 messages.
  143. /// The idle state can be interrupted any time to issue a command into the MMU
  144. class Idle : public ProtocolLogicPartBase {
  145. public:
  146. inline Idle(ProtocolLogic *logic)
  147. : ProtocolLogicPartBase(logic) {}
  148. void Restart() override;
  149. StepStatus Step() override;
  150. };
  151. /// The communication with the MMU is stopped/disabled (for whatever reason).
  152. /// Nothing is being put onto the UART.
  153. class Stopped : public ProtocolLogicPartBase {
  154. public:
  155. inline Stopped(ProtocolLogic *logic)
  156. : ProtocolLogicPartBase(logic) {}
  157. void Restart() override {}
  158. StepStatus Step() override { return Processing; }
  159. };
  160. ///< Filter of short consecutive drop outs which are recovered instantly
  161. class DropOutFilter {
  162. StepStatus cause;
  163. uint8_t occurrences;
  164. public:
  165. static constexpr uint8_t maxOccurrences = 10; // ideally set this to >8 seconds -> 12x heartBeatPeriod
  166. static_assert (maxOccurrences > 1, "we should really silently ignore at least 1 comm drop out if recovered immediately afterwards");
  167. DropOutFilter() = default;
  168. /// @returns true if the error should be reported to higher levels (max. number of consecutive occurrences reached)
  169. bool Record(StepStatus ss);
  170. /// @returns the initial cause which started this drop out event
  171. inline StepStatus InitialCause()const { return cause; }
  172. /// Rearms the object for further processing - basically call this once the MMU responds with something meaningful (e.g. S0 A2)
  173. inline void Reset(){ occurrences = maxOccurrences; }
  174. };
  175. /// Logic layer of the MMU vs. printer communication protocol
  176. class ProtocolLogic {
  177. public:
  178. ProtocolLogic(MMU2Serial *uart);
  179. /// Start/Enable communication with the MMU
  180. void Start();
  181. /// Stop/Disable communication with the MMU
  182. void Stop();
  183. // Issue commands to the MMU
  184. void ToolChange(uint8_t slot);
  185. void UnloadFilament();
  186. void LoadFilament(uint8_t slot);
  187. void EjectFilament(uint8_t slot);
  188. void CutFilament(uint8_t slot);
  189. void ResetMMU();
  190. void Button(uint8_t index);
  191. void Home(uint8_t mode);
  192. /// Step the state machine
  193. StepStatus Step();
  194. /// @returns the current/latest error code as reported by the MMU
  195. ErrorCode Error() const { return errorCode; }
  196. /// @returns the current/latest process code as reported by the MMU
  197. ProgressCode Progress() const { return progressCode; }
  198. /// @returns the current/latest button code as reported by the MMU
  199. Buttons Button() const { return buttonCode; }
  200. uint8_t CommandInProgress()const;
  201. inline bool Running()const {
  202. return state == State::Running;
  203. }
  204. inline bool FindaPressed() const {
  205. return findaPressed;
  206. }
  207. inline uint8_t MmuFwVersionMajor() const {
  208. return mmuFwVersionMajor;
  209. }
  210. inline uint8_t MmuFwVersionMinor() const {
  211. return mmuFwVersionMinor;
  212. }
  213. inline uint8_t MmuFwVersionBuild() const {
  214. return mmuFwVersionBuild;
  215. }
  216. #ifndef UNITTEST
  217. private:
  218. #endif
  219. StepStatus ExpectingMessage(uint32_t timeout);
  220. void SendMsg(RequestMsg rq);
  221. void SwitchToIdle();
  222. StepStatus SuppressShortDropOuts(const char *msg_P, StepStatus ss);
  223. StepStatus HandleCommunicationTimeout();
  224. StepStatus HandleProtocolError();
  225. bool Elapsed(uint32_t timeout) const;
  226. void RecordUARTActivity();
  227. void RecordReceivedByte(uint8_t c);
  228. void FormatLastReceivedBytes(char *dst);
  229. void FormatLastResponseMsgAndClearLRB(char *dst);
  230. void LogRequestMsg(const uint8_t *txbuff, uint8_t size);
  231. void LogError(const char *reason_P);
  232. void LogResponse();
  233. void SwitchFromIdleToCommand();
  234. void SwitchFromStartToIdle();
  235. enum class State : uint_fast8_t {
  236. Stopped, ///< stopped for whatever reason
  237. InitSequence, ///< initial sequence running
  238. Running ///< normal operation - Idle + Command processing
  239. };
  240. // individual sub-state machines - may be they can be combined into a union since only one is active at once
  241. Stopped stopped;
  242. StartSeq startSeq;
  243. DelayedRestart delayedRestart;
  244. Idle idle;
  245. Command command;
  246. ProtocolLogicPartBase *currentState; ///< command currently being processed
  247. /// Records the next planned state, "unknown" msg code if no command is planned.
  248. /// This is not intended to be a queue of commands to process, protocol_logic must not queue commands.
  249. /// It exists solely to prevent breaking the Request-Response protocol handshake -
  250. /// - during tests it turned out, that the commands from Marlin are coming in such an asynchronnous way, that
  251. /// we could accidentally send T2 immediately after Q0 without waiting for reception of response to Q0.
  252. ///
  253. /// Beware, if Marlin manages to call PlanGenericCommand multiple times before a response comes,
  254. /// these variables will get overwritten by the last call.
  255. /// However, that should not happen under normal circumstances as Marlin should wait for the Command to finish,
  256. /// which includes all responses (and error recovery if any).
  257. RequestMsg plannedRq;
  258. /// Plan a command to be processed once the immediate response to a sent request arrives
  259. void PlanGenericRequest(RequestMsg rq);
  260. /// Activate the planned state once the immediate response to a sent request arrived
  261. bool ActivatePlannedRequest();
  262. uint32_t lastUARTActivityMs; ///< timestamp - last ms when something occurred on the UART
  263. DropOutFilter dataTO; ///< Filter of short consecutive drop outs which are recovered instantly
  264. ResponseMsg rsp; ///< decoded response message from the MMU protocol
  265. State state; ///< internal state of ProtocolLogic
  266. Protocol protocol; ///< protocol codec
  267. array<uint8_t, 16> lastReceivedBytes; ///< remembers the last few bytes of incoming communication for diagnostic purposes
  268. uint8_t lrb;
  269. MMU2Serial *uart; ///< UART interface
  270. ErrorCode errorCode; ///< last received error code from the MMU
  271. ProgressCode progressCode; ///< last received progress code from the MMU
  272. Buttons buttonCode; ///< Last received button from the MMU.
  273. uint8_t lastFSensor; ///< last state of filament sensor
  274. bool findaPressed;
  275. uint8_t mmuFwVersionMajor, mmuFwVersionMinor;
  276. uint8_t mmuFwVersionBuild;
  277. friend class ProtocolLogicPartBase;
  278. friend class Stopped;
  279. friend class Command;
  280. friend class Idle;
  281. friend class StartSeq;
  282. friend class DelayedRestart;
  283. friend class MMU2;
  284. };
  285. } // namespace MMU2