| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 | 
							- #pragma once
 
- #include <stdint.h>
 
- #include <avr/pgmspace.h>
 
- // #include <array> //@@TODO Don't we have STL for AVR somewhere?
 
- template<typename T, uint8_t N>
 
- class array {
 
-     T data[N];
 
- public:
 
-     array() = default;
 
-     inline constexpr T* begin()const { return data; }
 
-     inline constexpr T* end()const { return data + N; }
 
-     static constexpr uint8_t size() { return N; }
 
-     inline T &operator[](uint8_t i){
 
-         return data[i];
 
-     }
 
- };
 
- #include "mmu2/error_codes.h"
 
- #include "mmu2/progress_codes.h"
 
- #include "mmu2/buttons.h"
 
- #include "mmu2_protocol.h"
 
- #include "mmu2_serial.h"
 
- /// New MMU2 protocol logic
 
- namespace MMU2 {
 
- using namespace modules::protocol;
 
- class ProtocolLogic;
 
- /// ProtocolLogic stepping statuses
 
- enum StepStatus : uint_fast8_t {
 
-     Processing = 0,
 
-     MessageReady, ///< a message has been successfully decoded from the received bytes
 
-     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)
 
-     CommandError,         ///< the command in progress stopped due to unrecoverable error, user interaction required
 
-     VersionMismatch,      ///< the MMU reports its firmware version incompatible with our implementation
 
-     CommunicationRecovered,
 
-     ButtonPushed, ///< The MMU reported the user pushed one of its three buttons.
 
- };
 
- static constexpr uint32_t linkLayerTimeout = 2000;                 ///< default link layer communication timeout
 
- static constexpr uint32_t dataLayerTimeout = linkLayerTimeout * 3; ///< data layer communication timeout
 
- static constexpr uint32_t heartBeatPeriod = linkLayerTimeout / 2;  ///< period of heart beat messages (Q0)
 
- static_assert(heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts");
 
- ///< Filter of short consecutive drop outs which are recovered instantly
 
- class DropOutFilter {
 
-     StepStatus cause;
 
-     uint8_t occurrences;
 
- public:
 
-     static constexpr uint8_t maxOccurrences = 10; // ideally set this to >8 seconds -> 12x heartBeatPeriod
 
-     static_assert(maxOccurrences > 1, "we should really silently ignore at least 1 comm drop out if recovered immediately afterwards");
 
-     DropOutFilter() = default;
 
-     /// @returns true if the error should be reported to higher levels (max. number of consecutive occurrences reached)
 
-     bool Record(StepStatus ss);
 
-     /// @returns the initial cause which started this drop out event
 
-     inline StepStatus InitialCause() const { return cause; }
 
-     /// Rearms the object for further processing - basically call this once the MMU responds with something meaningful (e.g. S0 A2)
 
-     inline void Reset() { occurrences = maxOccurrences; }
 
- };
 
- /// Logic layer of the MMU vs. printer communication protocol
 
- class ProtocolLogic {
 
- public:
 
-     ProtocolLogic(MMU2Serial *uart, uint8_t extraLoadDistance);
 
-     /// Start/Enable communication with the MMU
 
-     void Start();
 
-     /// Stop/Disable communication with the MMU
 
-     void Stop();
 
-     // Issue commands to the MMU
 
-     void ToolChange(uint8_t slot);
 
-     void Statistics();
 
-     void UnloadFilament();
 
-     void LoadFilament(uint8_t slot);
 
-     void EjectFilament(uint8_t slot);
 
-     void CutFilament(uint8_t slot);
 
-     void ResetMMU();
 
-     void Button(uint8_t index);
 
-     void Home(uint8_t mode);
 
-     void ReadRegister(uint8_t address);
 
-     void WriteRegister(uint8_t address, uint16_t data);
 
-     /// Sets the extra load distance to be reported to the MMU.
 
-     /// Beware - this call doesn't send anything to the MMU.
 
-     /// The MMU gets the newly set value either by a communication restart or via an explicit WriteRegister call
 
-     inline void PlanExtraLoadDistance(uint8_t eld_mm){
 
-         initRegs8[0] = eld_mm;
 
-     }
 
-     /// @returns the currently preset extra load distance
 
-     inline uint8_t ExtraLoadDistance()const {
 
-         return initRegs8[0];
 
-     }
 
-     /// Step the state machine
 
-     StepStatus Step();
 
-     /// @returns the current/latest error code as reported by the MMU
 
-     ErrorCode Error() const { return errorCode; }
 
-     /// @returns the current/latest process code as reported by the MMU
 
-     ProgressCode Progress() const { return progressCode; }
 
-     /// @returns the current/latest button code as reported by the MMU
 
-     Buttons Button() const { return buttonCode; }
 
-     uint8_t CommandInProgress() const;
 
-     inline bool Running() const {
 
-         return state == State::Running;
 
-     }
 
-     inline bool FindaPressed() const {
 
-         return regs8[0];
 
-     }
 
-     inline uint16_t FailStatistics() const {
 
-         return regs16[0];
 
-     }
 
-     inline uint8_t MmuFwVersionMajor() const {
 
-         return mmuFwVersion[0];
 
-     }
 
-     inline uint8_t MmuFwVersionMinor() const {
 
-         return mmuFwVersion[1];
 
-     }
 
-     inline uint8_t MmuFwVersionRevision() const {
 
-         return mmuFwVersion[2];
 
-     }
 
- #ifndef UNITTEST
 
- private:
 
- #endif
 
-     StepStatus ExpectingMessage();
 
-     void SendMsg(RequestMsg rq);
 
-     void SendWriteMsg(RequestMsg rq);
 
-     void SwitchToIdle();
 
-     StepStatus SuppressShortDropOuts(const char *msg_P, StepStatus ss);
 
-     StepStatus HandleCommunicationTimeout();
 
-     StepStatus HandleProtocolError();
 
-     bool Elapsed(uint32_t timeout) const;
 
-     void RecordUARTActivity();
 
-     void RecordReceivedByte(uint8_t c);
 
-     void FormatLastReceivedBytes(char *dst);
 
-     void FormatLastResponseMsgAndClearLRB(char *dst);
 
-     void LogRequestMsg(const uint8_t *txbuff, uint8_t size);
 
-     void LogError(const char *reason_P);
 
-     void LogResponse();
 
-     StepStatus SwitchFromIdleToCommand();
 
-     void SwitchFromStartToIdle();
 
-     enum class State : uint_fast8_t {
 
-         Stopped,      ///< stopped for whatever reason
 
-         InitSequence, ///< initial sequence running
 
-         Running       ///< normal operation - Idle + Command processing
 
-     };
 
-     // individual sub-state machines - may be they can be combined into a union since only one is active at once
 
-     // or we can blend them into ProtocolLogic at the cost of a less nice code (but hopefully shorter)
 
- //    Stopped stopped;
 
- //    StartSeq startSeq;
 
- //    DelayedRestart delayedRestart;
 
- //    Idle idle;
 
- //    Command command;
 
- //    ProtocolLogicPartBase *currentState; ///< command currently being processed
 
-     
 
-     enum class Scope : uint_fast8_t {
 
-         Stopped,
 
-         StartSeq,
 
-         DelayedRestart,
 
-         Idle,
 
-         Command
 
-     };
 
-     Scope currentScope;
 
-     // basic scope members
 
-     /// @returns true if the state machine is waiting for a response from the MMU
 
-     bool ExpectsResponse() const { return ((uint8_t)scopeState & (uint8_t)ScopeState::NotExpectsResponse) == 0; }
 
-     /// Common internal states of the derived sub-automata
 
-     /// General rule of thumb: *Sent states are waiting for a response from the MMU
 
-     enum class ScopeState : uint_fast8_t {
 
-         S0Sent, // beware - due to optimization reasons these SxSent must be kept one after another
 
-         S1Sent,
 
-         S2Sent,
 
-         S3Sent,
 
-         QuerySent,
 
-         CommandSent,
 
-         FilamentSensorStateSent,
 
-         Reading8bitRegisters,
 
-         Reading16bitRegisters,
 
-         WritingInitRegisters,
 
-         ButtonSent,
 
-         ReadRegisterSent, // standalone requests for reading registers - from higher layers
 
-         WriteRegisterSent,
 
-         // States which do not expect a message - MSb set
 
-         NotExpectsResponse = 0x80,
 
-         Wait = NotExpectsResponse + 1,
 
-         Ready = NotExpectsResponse + 2,
 
-         RecoveringProtocolError = NotExpectsResponse + 3,
 
-     };
 
-     ScopeState scopeState; ///< internal state of the sub-automaton
 
-     /// @returns the status of processing of the FINDA query response
 
-     /// @param finishedRV returned value in case the message was successfully received and processed
 
-     /// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
 
-     // StepStatus ProcessFINDAReqSent(StepStatus finishedRV, State nextState);
 
-     /// @returns the status of processing of the statistics query response
 
-     /// @param finishedRV returned value in case the message was successfully received and processed
 
-     /// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
 
-     // StepStatus ProcessStatisticsReqSent(StepStatus finishedRV, State nextState);
 
-     /// Called repeatedly while waiting for a query (Q0) period.
 
-     /// All event checks to report immediately from the printer to the MMU shall be done in this method.
 
-     /// So far, the only such a case is the filament sensor, but there can be more like this in the future.
 
-     void CheckAndReportAsyncEvents();
 
-     void SendQuery();
 
-     void StartReading8bitRegisters();
 
-     void ProcessRead8bitRegister();
 
-     void StartReading16bitRegisters();
 
-     ScopeState ProcessRead16bitRegister(ProtocolLogic::ScopeState stateAtEnd);
 
-     void StartWritingInitRegisters();
 
-     /// @returns true when all registers have been written into the MMU
 
-     bool ProcessWritingInitRegister();
 
-     void SendAndUpdateFilamentSensor();
 
-     void SendButton(uint8_t btn);
 
-     void SendVersion(uint8_t stage);
 
-     void SendReadRegister(uint8_t index, ScopeState nextState);
 
-     void SendWriteRegister(uint8_t index, uint16_t value, ScopeState nextState);
 
-     StepStatus ProcessVersionResponse(uint8_t stage);
 
-     /// Top level split - calls the appropriate step based on current scope
 
-     StepStatus ScopeStep();
 
-     static constexpr uint8_t maxRetries = 6;
 
-     uint8_t retries;
 
-     void StartSeqRestart();
 
-     void DelayedRestartRestart();
 
-     void IdleRestart();
 
-     void CommandRestart();
 
-     StepStatus StartSeqStep();
 
-     StepStatus DelayedRestartWait();
 
-     StepStatus IdleStep();
 
-     StepStatus IdleWait();
 
-     StepStatus CommandStep();
 
-     StepStatus CommandWait();
 
-     StepStatus StoppedStep() { return Processing; }
 
-     StepStatus ProcessCommandQueryResponse();
 
-     inline void SetRequestMsg(RequestMsg msg) {
 
-         rq = msg;
 
-     }
 
-     inline const RequestMsg &ReqMsg() const { return rq; }
 
-     RequestMsg rq = RequestMsg(RequestMsgCodes::unknown, 0);
 
-     /// Records the next planned state, "unknown" msg code if no command is planned.
 
-     /// This is not intended to be a queue of commands to process, protocol_logic must not queue commands.
 
-     /// It exists solely to prevent breaking the Request-Response protocol handshake -
 
-     /// - during tests it turned out, that the commands from Marlin are coming in such an asynchronnous way, that
 
-     /// we could accidentally send T2 immediately after Q0 without waiting for reception of response to Q0.
 
-     ///
 
-     /// Beware, if Marlin manages to call PlanGenericCommand multiple times before a response comes,
 
-     /// these variables will get overwritten by the last call.
 
-     /// However, that should not happen under normal circumstances as Marlin should wait for the Command to finish,
 
-     /// which includes all responses (and error recovery if any).
 
-     RequestMsg plannedRq;
 
-     /// Plan a command to be processed once the immediate response to a sent request arrives
 
-     void PlanGenericRequest(RequestMsg rq);
 
-     /// Activate the planned state once the immediate response to a sent request arrived
 
-     bool ActivatePlannedRequest();
 
-     uint32_t lastUARTActivityMs; ///< timestamp - last ms when something occurred on the UART
 
-     DropOutFilter dataTO;        ///< Filter of short consecutive drop outs which are recovered instantly
 
-     ResponseMsg rsp; ///< decoded response message from the MMU protocol
 
-     State state; ///< internal state of ProtocolLogic
 
-     Protocol protocol; ///< protocol codec
 
-     array<uint8_t, 16> lastReceivedBytes; ///< remembers the last few bytes of incoming communication for diagnostic purposes
 
-     uint8_t lrb;
 
-     MMU2Serial *uart; ///< UART interface
 
-     ErrorCode errorCode;       ///< last received error code from the MMU
 
-     ProgressCode progressCode; ///< last received progress code from the MMU
 
-     Buttons buttonCode;        ///< Last received button from the MMU.
 
-     uint8_t lastFSensor; ///< last state of filament sensor
 
-     // 8bit registers
 
-     static constexpr uint8_t regs8Count = 3;
 
-     static_assert(regs8Count > 0); // code is not ready for empty lists of registers
 
-     static const uint8_t regs8Addrs[regs8Count] PROGMEM;
 
-     uint8_t regs8[regs8Count];
 
-     // 16bit registers
 
-     static constexpr uint8_t regs16Count = 2;
 
-     static_assert(regs16Count > 0); // code is not ready for empty lists of registers
 
-     static const uint8_t regs16Addrs[regs16Count] PROGMEM;
 
-     uint16_t regs16[regs16Count];
 
-     // 8bit init values to be sent to the MMU after line up
 
-     static constexpr uint8_t initRegs8Count = 1;
 
-     static_assert(initRegs8Count > 0); // code is not ready for empty lists of registers
 
-     static const uint8_t initRegs8Addrs[initRegs8Count] PROGMEM;
 
-     uint8_t initRegs8[initRegs8Count];
 
-     uint8_t regIndex;
 
-     uint8_t mmuFwVersion[3];
 
-     uint16_t mmuFwVersionBuild;
 
-     friend class ProtocolLogicPartBase;
 
-     friend class Stopped;
 
-     friend class Command;
 
-     friend class Idle;
 
-     friend class StartSeq;
 
-     friend class DelayedRestart;
 
-     friend class MMU2;
 
- };
 
- } // namespace MMU2
 
 
  |