mmu2_protocol_logic.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. #include "mmu2_protocol_logic.h"
  2. #include "mmu2_log.h"
  3. #include "mmu2_fsensor.h"
  4. #include "system_timer.h"
  5. #include <string.h>
  6. namespace MMU2 {
  7. StepStatus ProtocolLogicPartBase::ProcessFINDAReqSent(StepStatus finishedRV, State nextState){
  8. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  9. if (expmsg != MessageReady)
  10. return expmsg;
  11. logic->findaPressed = logic->rsp.paramValue;
  12. state = nextState;
  13. return finishedRV;
  14. }
  15. void ProtocolLogicPartBase::CheckAndReportAsyncEvents(){
  16. // even when waiting for a query period, we need to report a change in filament sensor's state
  17. // - it is vital for a precise synchronization of moves of the printer and the MMU
  18. uint8_t fs = (uint8_t)WhereIsFilament();
  19. if( fs != logic->lastFSensor ){
  20. SendAndUpdateFilamentSensor();
  21. }
  22. }
  23. void ProtocolLogicPartBase::SendQuery(){
  24. logic->SendMsg(RequestMsg(RequestMsgCodes::Query, 0));
  25. state = State::QuerySent;
  26. }
  27. void ProtocolLogicPartBase::SendFINDAQuery(){
  28. logic->SendMsg(RequestMsg(RequestMsgCodes::Finda, 0 ) );
  29. state = State::FINDAReqSent;
  30. }
  31. void ProtocolLogicPartBase::SendAndUpdateFilamentSensor(){
  32. logic->SendMsg(RequestMsg(RequestMsgCodes::FilamentSensor, logic->lastFSensor = (uint8_t)WhereIsFilament() ) );
  33. state = State::FilamentSensorStateSent;
  34. }
  35. void ProtocolLogicPartBase::SendButton(uint8_t btn){
  36. logic->SendMsg(RequestMsg(RequestMsgCodes::Button, btn));
  37. state = State::ButtonSent;
  38. }
  39. StepStatus ProtocolLogic::ProcessUARTByte(uint8_t c) {
  40. switch (protocol.DecodeResponse(c)) {
  41. case DecodeStatus::MessageCompleted:
  42. return MessageReady;
  43. case DecodeStatus::NeedMoreData:
  44. return Processing;
  45. case DecodeStatus::Error:
  46. default:
  47. return ProtocolError;
  48. }
  49. }
  50. // searches for "ok\n" in the incoming serial data (that's the usual response of the old MMU FW)
  51. struct OldMMUFWDetector {
  52. uint8_t ok;
  53. inline constexpr OldMMUFWDetector():ok(0) { }
  54. enum class State : uint8_t { MatchingPart, SomethingElse, Matched };
  55. /// @returns true when "ok\n" gets detected
  56. State Detect(uint8_t c){
  57. // consume old MMU FW's data if any -> avoid confusion of protocol decoder
  58. if(ok == 0 && c == 'o'){
  59. ++ok;
  60. return State::MatchingPart;
  61. } else if(ok == 1 && c == 'k'){
  62. ++ok;
  63. return State::MatchingPart;
  64. } else if(ok == 2 && c == '\n'){
  65. return State::Matched;
  66. }
  67. return State::SomethingElse;
  68. }
  69. };
  70. StepStatus ProtocolLogic::ExpectingMessage(uint32_t timeout) {
  71. int bytesConsumed = 0;
  72. int c = -1;
  73. OldMMUFWDetector oldMMUh4x0r; // old MMU FW hacker ;)
  74. // try to consume as many rx bytes as possible (until a message has been completed)
  75. while((c = uart->read()) >= 0){
  76. ++bytesConsumed;
  77. RecordReceivedByte(c);
  78. switch (protocol.DecodeResponse(c)) {
  79. case DecodeStatus::MessageCompleted:
  80. rsp = protocol.GetResponseMsg();
  81. LogResponse();
  82. RecordUARTActivity(); // something has happened on the UART, update the timeout record
  83. return MessageReady;
  84. case DecodeStatus::NeedMoreData:
  85. break;
  86. case DecodeStatus::Error:{
  87. // consume old MMU FW's data if any -> avoid confusion of protocol decoder
  88. auto old = oldMMUh4x0r.Detect(c);
  89. if( old == OldMMUFWDetector::State::Matched ){
  90. // hack bad FW version - BEWARE - we silently assume that the first query is an "S0"
  91. // The old MMU FW responds with "ok\n" and we fake the response to a bad FW version at this spot
  92. rsp = ResponseMsg(RequestMsg(RequestMsgCodes::Version, 0), ResponseMsgParamCodes::Accepted, 0);
  93. return MessageReady;
  94. } else if( old == OldMMUFWDetector::State::MatchingPart ){
  95. break;
  96. }
  97. }
  98. // otherwise [[fallthrough]]
  99. default:
  100. RecordUARTActivity(); // something has happened on the UART, update the timeout record
  101. return ProtocolError;
  102. }
  103. }
  104. if( bytesConsumed != 0 ){
  105. RecordUARTActivity(); // something has happened on the UART, update the timeout record
  106. return Processing; // consumed some bytes, but message still not ready
  107. } else if (Elapsed(timeout)) {
  108. return CommunicationTimeout;
  109. }
  110. return Processing;
  111. }
  112. void ProtocolLogic::SendMsg(RequestMsg rq) {
  113. uint8_t txbuff[Protocol::MaxRequestSize()];
  114. uint8_t len = Protocol::EncodeRequest(rq, txbuff);
  115. uart->write(txbuff, len);
  116. LogRequestMsg(txbuff, len);
  117. RecordUARTActivity();
  118. }
  119. void StartSeq::Restart() {
  120. state = State::S0Sent;
  121. logic->SendMsg(RequestMsg(RequestMsgCodes::Version, 0));
  122. }
  123. StepStatus StartSeq::Step() {
  124. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  125. if (expmsg != MessageReady)
  126. return expmsg;
  127. // solve initial handshake
  128. switch (state) {
  129. case State::S0Sent: // received response to S0 - major
  130. logic->mmuFwVersionMajor = logic->rsp.paramValue;
  131. if (logic->mmuFwVersionMajor != 2) {
  132. return VersionMismatch;
  133. }
  134. logic->dataTO.Reset(); // got meaningful response from the MMU, stop data layer timeout tracking
  135. logic->SendMsg(RequestMsg(RequestMsgCodes::Version, 1));
  136. state = State::S1Sent;
  137. break;
  138. case State::S1Sent: // received response to S1 - minor
  139. logic->mmuFwVersionMinor = logic->rsp.paramValue;
  140. if (logic->mmuFwVersionMinor != 0) {
  141. return VersionMismatch;
  142. }
  143. logic->SendMsg(RequestMsg(RequestMsgCodes::Version, 2));
  144. state = State::S2Sent;
  145. break;
  146. case State::S2Sent: // received response to S2 - revision
  147. logic->mmuFwVersionBuild = logic->rsp.paramValue;
  148. if (logic->mmuFwVersionBuild < 18) {
  149. return VersionMismatch;
  150. }
  151. // Start General Interrogation after line up.
  152. // For now we just send the state of the filament sensor, but we may request
  153. // data point states from the MMU as well. TBD in the future, especially with another protocol
  154. SendAndUpdateFilamentSensor();
  155. break;
  156. case State::FilamentSensorStateSent:
  157. state = State::Ready;
  158. return Finished;
  159. break;
  160. default:
  161. return VersionMismatch;
  162. }
  163. return Processing;
  164. }
  165. void Command::Restart() {
  166. state = State::CommandSent;
  167. logic->SendMsg(logic->command.rq);
  168. }
  169. StepStatus Command::Step() {
  170. switch (state) {
  171. case State::CommandSent: {
  172. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  173. if (expmsg != MessageReady)
  174. return expmsg;
  175. switch (logic->rsp.paramCode) { // the response should be either accepted or rejected
  176. case ResponseMsgParamCodes::Accepted:
  177. logic->progressCode = ProgressCode::OK;
  178. logic->errorCode = ErrorCode::RUNNING;
  179. state = State::Wait;
  180. break;
  181. case ResponseMsgParamCodes::Rejected:
  182. // rejected - should normally not happen, but report the error up
  183. logic->progressCode = ProgressCode::OK;
  184. logic->errorCode = ErrorCode::PROTOCOL_ERROR;
  185. return CommandRejected;
  186. default:
  187. return ProtocolError;
  188. }
  189. } break;
  190. case State::Wait:
  191. if (logic->Elapsed(heartBeatPeriod)) {
  192. SendQuery();
  193. } else {
  194. // even when waiting for a query period, we need to report a change in filament sensor's state
  195. // - it is vital for a precise synchronization of moves of the printer and the MMU
  196. CheckAndReportAsyncEvents();
  197. }
  198. break;
  199. case State::QuerySent: {
  200. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  201. if (expmsg != MessageReady)
  202. return expmsg;
  203. }
  204. // [[fallthrough]];
  205. case State::ContinueFromIdle:
  206. switch (logic->rsp.paramCode) {
  207. case ResponseMsgParamCodes::Processing:
  208. logic->progressCode = static_cast<ProgressCode>(logic->rsp.paramValue);
  209. logic->errorCode = ErrorCode::OK;
  210. SendAndUpdateFilamentSensor(); // keep on reporting the state of fsensor regularly
  211. break;
  212. case ResponseMsgParamCodes::Error:
  213. // in case of an error the progress code remains as it has been before
  214. logic->errorCode = static_cast<ErrorCode>(logic->rsp.paramValue);
  215. // keep on reporting the state of fsensor regularly even in command error state
  216. // - the MMU checks FINDA and fsensor even while recovering from errors
  217. SendAndUpdateFilamentSensor();
  218. return CommandError;
  219. case ResponseMsgParamCodes::Button:
  220. // The user pushed a button on the MMU. Save it, do what we need to do
  221. // to prepare, then pass it back to the MMU so it can work its magic.
  222. logic->buttonCode = static_cast<Buttons>(logic->rsp.paramValue);
  223. SendAndUpdateFilamentSensor();
  224. return ButtonPushed;
  225. case ResponseMsgParamCodes::Finished:
  226. logic->progressCode = ProgressCode::OK;
  227. state = State::Ready;
  228. return Finished;
  229. default:
  230. return ProtocolError;
  231. }
  232. break;
  233. case State::FilamentSensorStateSent:{
  234. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  235. if (expmsg != MessageReady)
  236. return expmsg;
  237. SendFINDAQuery();
  238. } break;
  239. case State::FINDAReqSent:
  240. return ProcessFINDAReqSent(Processing, State::Wait);
  241. case State::ButtonSent:{
  242. // button is never confirmed ... may be it should be
  243. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  244. if (expmsg != MessageReady)
  245. return expmsg;
  246. if (logic->rsp.paramCode == ResponseMsgParamCodes::Accepted)
  247. {
  248. // Button was accepted, decrement the retry.
  249. mmu2.DecrementRetryAttempts();
  250. }
  251. SendAndUpdateFilamentSensor();
  252. } break;
  253. default:
  254. return ProtocolError;
  255. }
  256. return Processing;
  257. }
  258. void Idle::Restart() {
  259. state = State::Ready;
  260. }
  261. StepStatus Idle::Step() {
  262. switch (state) {
  263. case State::Ready: // check timeout
  264. if (logic->Elapsed(heartBeatPeriod)) {
  265. SendQuery();
  266. return Processing;
  267. }
  268. break;
  269. case State::QuerySent: { // check UART
  270. auto expmsg = logic->ExpectingMessage(linkLayerTimeout);
  271. if (expmsg != MessageReady)
  272. return expmsg;
  273. // If we are accidentally in Idle and we receive something like "T0 P1" - that means the communication dropped out while a command was in progress.
  274. // That causes no issues here, we just need to switch to Command processing and continue there from now on.
  275. // The usual response in this case should be some command and "F" - finished - that confirms we are in an Idle state even on the MMU side.
  276. switch( logic->rsp.request.code ){
  277. case RequestMsgCodes::Cut:
  278. case RequestMsgCodes::Eject:
  279. case RequestMsgCodes::Load:
  280. case RequestMsgCodes::Mode:
  281. case RequestMsgCodes::Tool:
  282. case RequestMsgCodes::Unload:
  283. if( logic->rsp.paramCode != ResponseMsgParamCodes::Finished ){
  284. logic->SwitchFromIdleToCommand();
  285. return Processing;
  286. }
  287. break;
  288. case RequestMsgCodes::Reset:
  289. // this one is kind of special
  290. // we do not transfer to any "running" command (i.e. we stay in Idle),
  291. // but in case there is an error reported we must make sure it gets propagated
  292. switch( logic->rsp.paramCode ){
  293. case ResponseMsgParamCodes::Processing:
  294. // @@TODO we may actually use this branch to report progress of manual operation on the MMU
  295. // The MMU sends e.g. X0 P27 after its restart when the user presses an MMU button to move the Selector
  296. // For now let's behave just like "finished"
  297. case ResponseMsgParamCodes::Finished:
  298. logic->errorCode = ErrorCode::OK;
  299. break;
  300. default:
  301. logic->errorCode = static_cast<ErrorCode>(logic->rsp.paramValue);
  302. SendFINDAQuery(); // continue Idle state without restarting the communication
  303. return CommandError;
  304. }
  305. break;
  306. default:
  307. break;
  308. }
  309. SendFINDAQuery();
  310. return Processing;
  311. } break;
  312. case State::FINDAReqSent:
  313. return ProcessFINDAReqSent(Finished, State::Ready);
  314. default:
  315. return ProtocolError;
  316. }
  317. // The "return Finished" in this state machine requires a bit of explanation:
  318. // The Idle state either did nothing (still waiting for the heartbeat timeout)
  319. // or just successfully received the answer to Q0, whatever that was.
  320. // In both cases, it is ready to hand over work to a command or something else,
  321. // therefore we are returning Finished (also to exit mmu_loop() and unblock Marlin's loop!).
  322. // If there is no work, we'll end up in the Idle state again
  323. // and we'll send the heartbeat message after the specified timeout.
  324. return Finished;
  325. }
  326. ProtocolLogic::ProtocolLogic(MMU2Serial *uart)
  327. : stopped(this)
  328. , startSeq(this)
  329. , idle(this)
  330. , command(this)
  331. , currentState(&stopped)
  332. , plannedRq(RequestMsgCodes::unknown, 0)
  333. , lastUARTActivityMs(0)
  334. , rsp(RequestMsg(RequestMsgCodes::unknown, 0), ResponseMsgParamCodes::unknown, 0)
  335. , state(State::Stopped)
  336. , lrb(0)
  337. , uart(uart)
  338. , lastFSensor((uint8_t)WhereIsFilament())
  339. {}
  340. void ProtocolLogic::Start() {
  341. state = State::InitSequence;
  342. currentState = &startSeq;
  343. startSeq.Restart();
  344. }
  345. void ProtocolLogic::Stop() {
  346. state = State::Stopped;
  347. currentState = &stopped;
  348. }
  349. void ProtocolLogic::ToolChange(uint8_t slot) {
  350. PlanGenericRequest(RequestMsg(RequestMsgCodes::Tool, slot));
  351. }
  352. void ProtocolLogic::UnloadFilament() {
  353. PlanGenericRequest(RequestMsg(RequestMsgCodes::Unload, 0));
  354. }
  355. void ProtocolLogic::LoadFilament(uint8_t slot) {
  356. PlanGenericRequest(RequestMsg(RequestMsgCodes::Load, slot));
  357. }
  358. void ProtocolLogic::EjectFilament(uint8_t slot) {
  359. PlanGenericRequest(RequestMsg(RequestMsgCodes::Eject, slot));
  360. }
  361. void ProtocolLogic::CutFilament(uint8_t slot){
  362. PlanGenericRequest(RequestMsg(RequestMsgCodes::Cut, slot));
  363. }
  364. void ProtocolLogic::ResetMMU() {
  365. PlanGenericRequest(RequestMsg(RequestMsgCodes::Reset, 0));
  366. }
  367. void ProtocolLogic::Button(uint8_t index){
  368. PlanGenericRequest(RequestMsg(RequestMsgCodes::Button, index));
  369. }
  370. void ProtocolLogic::Home(uint8_t mode){
  371. PlanGenericRequest(RequestMsg(RequestMsgCodes::Home, mode));
  372. }
  373. void ProtocolLogic::PlanGenericRequest(RequestMsg rq) {
  374. plannedRq = rq;
  375. if( ! currentState->ExpectsResponse() ){
  376. ActivatePlannedRequest();
  377. } // otherwise wait for an empty window to activate the request
  378. }
  379. bool ProtocolLogic::ActivatePlannedRequest(){
  380. if( plannedRq.code == RequestMsgCodes::Button ){
  381. // only issue the button to the MMU and do not restart the state machines
  382. // @@TODO - this is not completely correct, but it does the job.
  383. // In Idle mode the command part is not active, but we still need button handling in Idle mode (resolve MMU init errors)
  384. // -> command.SendButton is not correct, but it sends the message and everything works (for now)
  385. command.SendButton(plannedRq.value);
  386. plannedRq = RequestMsg(RequestMsgCodes::unknown, 0);
  387. return true;
  388. } else if( plannedRq.code != RequestMsgCodes::unknown ){
  389. currentState = &command;
  390. command.SetRequestMsg(plannedRq);
  391. plannedRq = RequestMsg(RequestMsgCodes::unknown, 0);
  392. command.Restart();
  393. return true;
  394. }
  395. return false;
  396. }
  397. void ProtocolLogic::SwitchFromIdleToCommand(){
  398. currentState = &command;
  399. command.SetRequestMsg(rsp.request);
  400. // we are recovering from a communication drop out, the command is already running
  401. // and we have just received a response to a Q0 message about a command progress
  402. command.ContinueFromIdle();
  403. }
  404. void ProtocolLogic::SwitchToIdle() {
  405. state = State::Running;
  406. currentState = &idle;
  407. idle.Restart();
  408. }
  409. void ProtocolLogic::HandleCommunicationTimeout() {
  410. uart->flush(); // clear the output buffer
  411. currentState = &startSeq;
  412. state = State::InitSequence;
  413. startSeq.Restart();
  414. }
  415. bool ProtocolLogic::Elapsed(uint32_t timeout) const {
  416. return _millis() >= (lastUARTActivityMs + timeout);
  417. }
  418. void ProtocolLogic::RecordUARTActivity() {
  419. lastUARTActivityMs = _millis();
  420. }
  421. void ProtocolLogic::RecordReceivedByte(uint8_t c){
  422. lastReceivedBytes[lrb] = c;
  423. lrb = (lrb+1) % lastReceivedBytes.size();
  424. }
  425. constexpr char NibbleToChar(uint8_t c){
  426. switch (c) {
  427. case 0:
  428. case 1:
  429. case 2:
  430. case 3:
  431. case 4:
  432. case 5:
  433. case 6:
  434. case 7:
  435. case 8:
  436. case 9:
  437. return c + '0';
  438. case 10:
  439. case 11:
  440. case 12:
  441. case 13:
  442. case 14:
  443. case 15:
  444. return (c - 10) + 'a';
  445. default:
  446. return 0;
  447. }
  448. }
  449. void ProtocolLogic::FormatLastReceivedBytes(char *dst){
  450. for(uint8_t i = 0; i < lastReceivedBytes.size(); ++i){
  451. uint8_t b = lastReceivedBytes[ (lrb-i-1) % lastReceivedBytes.size() ];
  452. dst[i*3] = NibbleToChar(b >> 4);
  453. dst[i*3+1] = NibbleToChar(b & 0xf);
  454. dst[i*3+2] = ' ';
  455. }
  456. dst[ (lastReceivedBytes.size() - 1) * 3 + 2] = 0; // terminate properly
  457. }
  458. void ProtocolLogic::FormatLastResponseMsgAndClearLRB(char *dst){
  459. *dst++ = '<';
  460. for(uint8_t i = 0; i < lrb; ++i){
  461. uint8_t b = lastReceivedBytes[ i ];
  462. if( b < 32 )b = '.';
  463. if( b > 127 )b = '.';
  464. *dst++ = b;
  465. }
  466. *dst = 0; // terminate properly
  467. lrb = 0; // reset the input buffer index in case of a clean message
  468. }
  469. void ProtocolLogic::LogRequestMsg(const uint8_t *txbuff, uint8_t size){
  470. constexpr uint_fast8_t rqs = modules::protocol::Protocol::MaxRequestSize() + 2;
  471. char tmp[rqs] = ">";
  472. static char lastMsg[rqs] = "";
  473. for(uint8_t i = 0; i < size; ++i){
  474. uint8_t b = txbuff[i];
  475. if( b < 32 )b = '.';
  476. if( b > 127 )b = '.';
  477. tmp[i+1] = b;
  478. }
  479. tmp[size+1] = '\n';
  480. tmp[size+2] = 0;
  481. if( !strncmp(tmp, ">S0.\n", rqs) && !strncmp(lastMsg, tmp, rqs) ){
  482. // @@TODO we skip the repeated request msgs for now
  483. // to avoid spoiling the whole log just with ">S0" messages
  484. // especially when the MMU is not connected.
  485. // We'll lose the ability to see if the printer is actually
  486. // trying to find the MMU, but since it has been reliable in the past
  487. // we can live without it for now.
  488. } else {
  489. MMU2_ECHO_MSG(tmp);
  490. }
  491. memcpy(lastMsg, tmp, rqs);
  492. }
  493. void ProtocolLogic::LogError(const char *reason){
  494. char lrb[lastReceivedBytes.size() * 3];
  495. FormatLastReceivedBytes(lrb);
  496. MMU2_ERROR_MSG(reason);
  497. SERIAL_ECHO(", last bytes: ");
  498. SERIAL_ECHOLN(lrb);
  499. }
  500. void ProtocolLogic::LogResponse(){
  501. char lrb[lastReceivedBytes.size()];
  502. FormatLastResponseMsgAndClearLRB(lrb);
  503. MMU2_ECHO_MSG(lrb);
  504. SERIAL_ECHOLN();
  505. }
  506. StepStatus ProtocolLogic::HandleCommError(const char *msg, StepStatus ss){
  507. protocol.ResetResponseDecoder();
  508. HandleCommunicationTimeout();
  509. if( dataTO.Record(ss) ){
  510. LogError(msg);
  511. return dataTO.InitialCause();
  512. } else {
  513. return Processing; // suppress short drop outs of communication
  514. }
  515. }
  516. StepStatus ProtocolLogic::Step() {
  517. if( ! currentState->ExpectsResponse() ){ // if not waiting for a response, activate a planned request immediately
  518. ActivatePlannedRequest();
  519. }
  520. auto currentStatus = currentState->Step();
  521. switch (currentStatus) {
  522. case Processing:
  523. // we are ok, the state machine continues correctly
  524. break;
  525. case Finished: {
  526. // We are ok, switching to Idle if there is no potential next request planned.
  527. // But the trouble is we must report a finished command if the previous command has just been finished
  528. // i.e. only try to find some planned command if we just finished the Idle cycle
  529. bool previousCommandFinished = currentState == &command; // @@TODO this is a nasty hack :(
  530. if( ! ActivatePlannedRequest() ){ // if nothing is planned, switch to Idle
  531. SwitchToIdle();
  532. } else {
  533. // if the previous cycle was Idle and now we have planned a new command -> avoid returning Finished
  534. if( ! previousCommandFinished && currentState == &command){
  535. currentStatus = Processing;
  536. }
  537. }
  538. }
  539. break;
  540. case CommandRejected:
  541. // we have to repeat it - that's the only thing we can do
  542. // no change in state
  543. // @@TODO wait until Q0 returns command in progress finished, then we can send this one
  544. LogError("Command rejected");
  545. command.Restart();
  546. break;
  547. case CommandError:
  548. LogError("Command Error");
  549. // we shall probably transfer into the Idle state and await further instructions from the upper layer
  550. // Idle state may solve the problem of keeping up the heart beat running
  551. break;
  552. case VersionMismatch:
  553. LogError("Version mismatch");
  554. Stop(); // cannot continue
  555. break;
  556. case ProtocolError:
  557. currentStatus = HandleCommError("Protocol error", ProtocolError);
  558. break;
  559. case CommunicationTimeout:
  560. currentStatus = HandleCommError("Communication timeout", CommunicationTimeout);
  561. break;
  562. default:
  563. break;
  564. }
  565. return currentStatus;
  566. }
  567. uint8_t ProtocolLogic::CommandInProgress() const {
  568. if( currentState != &command )
  569. return 0;
  570. return (uint8_t)command.ReqMsg().code;
  571. }
  572. bool DropOutFilter::Record(StepStatus ss){
  573. if( occurrences == maxOccurrences ){
  574. cause = ss;
  575. }
  576. --occurrences;
  577. return occurrences == 0;
  578. }
  579. } // namespace MMU2