mmu2_error_converter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #include "mmu2_error_converter.h"
  2. #include "mmu2/error_codes.h"
  3. #include "mmu2/errors_list.h"
  4. #include "language.h"
  5. #include <stdio.h>
  6. namespace MMU2 {
  7. static ButtonOperations buttonSelectedOperation = ButtonOperations::NoOperation;
  8. // we don't have a constexpr find_if in C++17/STL yet
  9. template <class InputIt, class UnaryPredicate>
  10. constexpr InputIt find_if_cx(InputIt first, InputIt last, UnaryPredicate p) {
  11. for (; first != last; ++first) {
  12. if (p(*first)) {
  13. return first;
  14. }
  15. }
  16. return last;
  17. }
  18. // Making a constexpr FindError should instruct the compiler to optimize the ConvertMMUErrorCode
  19. // in such a way that no searching will ever be done at runtime.
  20. // A call to FindError then compiles to a single instruction even on the AVR.
  21. static constexpr uint8_t FindErrorIndex(uint16_t pec) {
  22. constexpr uint16_t errorCodesSize = sizeof(errorCodes) / sizeof(errorCodes[0]);
  23. constexpr const auto *errorCodesEnd = errorCodes + errorCodesSize;
  24. const auto *i = find_if_cx(errorCodes, errorCodesEnd, [pec](uint16_t ed){ return ed == pec; });
  25. return (i != errorCodesEnd) ? (i-errorCodes) : (errorCodesSize - 1);
  26. }
  27. // check that the searching algoritm works
  28. static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER) == 0);
  29. static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_GO_OFF) == 1);
  30. static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER) == 2);
  31. static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_GO_OFF) == 3);
  32. uint8_t PrusaErrorCodeIndex(uint16_t ec) {
  33. switch (ec) {
  34. case (uint16_t)ErrorCode::FINDA_DIDNT_SWITCH_ON:
  35. return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER);
  36. case (uint16_t)ErrorCode::FINDA_DIDNT_SWITCH_OFF:
  37. return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_GO_OFF);
  38. case (uint16_t)ErrorCode::FSENSOR_DIDNT_SWITCH_ON:
  39. return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER);
  40. case (uint16_t)ErrorCode::FSENSOR_DIDNT_SWITCH_OFF:
  41. return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_GO_OFF);
  42. case (uint16_t)ErrorCode::FSENSOR_TOO_EARLY:
  43. return FindErrorIndex(ERR_MECHANICAL_FSENSOR_TOO_EARLY);
  44. case (uint16_t)ErrorCode::FINDA_FLICKERS:
  45. return FindErrorIndex(ERR_MECHANICAL_INSPECT_FINDA);
  46. case (uint16_t)ErrorCode::STALLED_PULLEY:
  47. case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED:
  48. return FindErrorIndex(ERR_MECHANICAL_PULLEY_CANNOT_MOVE);
  49. case (uint16_t)ErrorCode::HOMING_SELECTOR_FAILED:
  50. return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_HOME);
  51. case (uint16_t)ErrorCode::MOVE_SELECTOR_FAILED:
  52. return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_MOVE);
  53. case (uint16_t)ErrorCode::HOMING_IDLER_FAILED:
  54. return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_HOME);
  55. case (uint16_t)ErrorCode::MOVE_IDLER_FAILED:
  56. return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_MOVE);
  57. case (uint16_t)ErrorCode::MMU_NOT_RESPONDING:
  58. return FindErrorIndex(ERR_CONNECT_MMU_NOT_RESPONDING);
  59. case (uint16_t)ErrorCode::PROTOCOL_ERROR:
  60. return FindErrorIndex(ERR_CONNECT_COMMUNICATION_ERROR);
  61. case (uint16_t)ErrorCode::FILAMENT_ALREADY_LOADED:
  62. return FindErrorIndex(ERR_SYSTEM_FILAMENT_ALREADY_LOADED);
  63. case (uint16_t)ErrorCode::INVALID_TOOL:
  64. return FindErrorIndex(ERR_SYSTEM_INVALID_TOOL);
  65. case (uint16_t)ErrorCode::QUEUE_FULL:
  66. return FindErrorIndex(ERR_SYSTEM_QUEUE_FULL);
  67. case (uint16_t)ErrorCode::VERSION_MISMATCH:
  68. return FindErrorIndex(ERR_SYSTEM_FW_UPDATE_NEEDED);
  69. case (uint16_t)ErrorCode::INTERNAL:
  70. return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR);
  71. case (uint16_t)ErrorCode::FINDA_VS_EEPROM_DISREPANCY:
  72. return FindErrorIndex(ERR_SYSTEM_UNLOAD_MANUALLY);
  73. }
  74. // Electrical issues which can be detected somehow.
  75. // Need to be placed before TMC-related errors in order to process couples of error bits between single ones
  76. // and to keep the code size down.
  77. if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) {
  78. if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
  79. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_SELFTEST_FAILED);
  80. } else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) {
  81. if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
  82. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_SELFTEST_FAILED);
  83. } else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) {
  84. if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
  85. return FindErrorIndex(ERR_ELECTRICAL_IDLER_SELFTEST_FAILED);
  86. }
  87. // TMC-related errors - multiple of these can occur at once
  88. // - in such a case we report the first which gets found/converted into Prusa-Error-Codes (usually the fact, that one TMC has an issue is serious enough)
  89. // By carefully ordering the checks here we can prioritize the errors being reported to the user.
  90. if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) {
  91. if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH)
  92. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_ERROR);
  93. if (ec & (uint16_t)ErrorCode::TMC_RESET)
  94. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_RESET);
  95. if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)
  96. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_UNDERVOLTAGE_ERROR);
  97. if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND)
  98. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_SHORTED);
  99. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN)
  100. return FindErrorIndex(ERR_TEMPERATURE_PULLEY_WARNING_TMC_TOO_HOT);
  101. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR)
  102. return FindErrorIndex(ERR_TEMPERATURE_PULLEY_TMC_OVERHEAT_ERROR);
  103. } else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) {
  104. if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH)
  105. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_ERROR);
  106. if (ec & (uint16_t)ErrorCode::TMC_RESET)
  107. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_RESET);
  108. if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)
  109. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_UNDERVOLTAGE_ERROR);
  110. if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND)
  111. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_SHORTED);
  112. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN)
  113. return FindErrorIndex(ERR_TEMPERATURE_SELECTOR_WARNING_TMC_TOO_HOT);
  114. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR)
  115. return FindErrorIndex(ERR_TEMPERATURE_SELECTOR_TMC_OVERHEAT_ERROR);
  116. } else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) {
  117. if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH)
  118. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_ERROR);
  119. if (ec & (uint16_t)ErrorCode::TMC_RESET)
  120. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_RESET);
  121. if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)
  122. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_UNDERVOLTAGE_ERROR);
  123. if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND)
  124. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_SHORTED);
  125. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN)
  126. return FindErrorIndex(ERR_TEMPERATURE_IDLER_WARNING_TMC_TOO_HOT);
  127. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR)
  128. return FindErrorIndex(ERR_TEMPERATURE_IDLER_TMC_OVERHEAT_ERROR);
  129. }
  130. // if nothing got caught, return a generic runtime error
  131. return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR);
  132. }
  133. uint16_t PrusaErrorCode(uint8_t i){
  134. return pgm_read_word(errorCodes + i);
  135. }
  136. const char * PrusaErrorTitle(uint8_t i){
  137. return (const char *)pgm_read_ptr(errorTitles + i);
  138. }
  139. const char * PrusaErrorDesc(uint8_t i){
  140. return (const char *)pgm_read_ptr(errorDescs + i);
  141. }
  142. uint8_t PrusaErrorButtons(uint8_t i){
  143. return pgm_read_byte(errorButtons + i);
  144. }
  145. const char * PrusaErrorButtonTitle(uint8_t bi){
  146. // -1 represents the hidden NoOperation button which is not drawn in any way
  147. return (const char *)pgm_read_ptr(btnOperation + bi - 1);
  148. }
  149. const char * PrusaErrorButtonMore(){
  150. return _R(MSG_BTN_MORE);//@todo convert to PROGMEM_N1
  151. }
  152. struct ResetOnExit {
  153. ResetOnExit() = default;
  154. ~ResetOnExit(){
  155. buttonSelectedOperation = ButtonOperations::NoOperation;
  156. }
  157. };
  158. Buttons ButtonPressed(uint16_t ec) {
  159. if (buttonSelectedOperation == ButtonOperations::NoOperation) {
  160. return NoButton; // no button
  161. }
  162. ResetOnExit ros; // clear buttonSelectedOperation on exit from this call
  163. return ButtonAvailable(ec);
  164. }
  165. Buttons ButtonAvailable(uint16_t ec) {
  166. uint8_t ei = PrusaErrorCodeIndex(ec);
  167. // The list of responses which occur in mmu error dialogs
  168. // Return button index or perform some action on the MK3 by itself (like restart MMU)
  169. // Based on Prusa-Error-Codes errors_list.h
  170. // So far hardcoded, but shall be generated in the future
  171. switch ( PrusaErrorCode(ei) ) {
  172. case ERR_MECHANICAL_FINDA_DIDNT_TRIGGER:
  173. case ERR_MECHANICAL_FINDA_DIDNT_GO_OFF:
  174. switch (buttonSelectedOperation) {
  175. case ButtonOperations::Retry: // "Repeat action"
  176. return Middle;
  177. case ButtonOperations::Continue: // "Continue"
  178. return Right;
  179. default:
  180. break;
  181. }
  182. break;
  183. case ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER:
  184. case ERR_MECHANICAL_FSENSOR_DIDNT_GO_OFF:
  185. case ERR_MECHANICAL_FSENSOR_TOO_EARLY:
  186. case ERR_MECHANICAL_INSPECT_FINDA:
  187. case ERR_MECHANICAL_SELECTOR_CANNOT_HOME:
  188. case ERR_MECHANICAL_SELECTOR_CANNOT_MOVE:
  189. case ERR_MECHANICAL_IDLER_CANNOT_HOME:
  190. case ERR_MECHANICAL_IDLER_CANNOT_MOVE:
  191. case ERR_MECHANICAL_PULLEY_CANNOT_MOVE:
  192. case ERR_SYSTEM_UNLOAD_MANUALLY:
  193. switch (buttonSelectedOperation) {
  194. // may be allow move selector right and left in the future
  195. case ButtonOperations::Retry: // "Repeat action"
  196. return Middle;
  197. default:
  198. break;
  199. }
  200. break;
  201. case ERR_TEMPERATURE_PULLEY_WARNING_TMC_TOO_HOT:
  202. case ERR_TEMPERATURE_SELECTOR_WARNING_TMC_TOO_HOT:
  203. case ERR_TEMPERATURE_IDLER_WARNING_TMC_TOO_HOT:
  204. switch (buttonSelectedOperation) {
  205. case ButtonOperations::Continue: // "Continue"
  206. return Left;
  207. case ButtonOperations::RestartMMU: // "Restart MMU"
  208. return RestartMMU;
  209. default:
  210. break;
  211. }
  212. break;
  213. case ERR_TEMPERATURE_PULLEY_TMC_OVERHEAT_ERROR:
  214. case ERR_TEMPERATURE_SELECTOR_TMC_OVERHEAT_ERROR:
  215. case ERR_TEMPERATURE_IDLER_TMC_OVERHEAT_ERROR:
  216. case ERR_ELECTRICAL_PULLEY_TMC_DRIVER_ERROR:
  217. case ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_ERROR:
  218. case ERR_ELECTRICAL_IDLER_TMC_DRIVER_ERROR:
  219. case ERR_ELECTRICAL_PULLEY_TMC_DRIVER_RESET:
  220. case ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_RESET:
  221. case ERR_ELECTRICAL_IDLER_TMC_DRIVER_RESET:
  222. case ERR_ELECTRICAL_PULLEY_TMC_UNDERVOLTAGE_ERROR:
  223. case ERR_ELECTRICAL_SELECTOR_TMC_UNDERVOLTAGE_ERROR:
  224. case ERR_ELECTRICAL_IDLER_TMC_UNDERVOLTAGE_ERROR:
  225. case ERR_ELECTRICAL_PULLEY_TMC_DRIVER_SHORTED:
  226. case ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_SHORTED:
  227. case ERR_ELECTRICAL_IDLER_TMC_DRIVER_SHORTED:
  228. case ERR_ELECTRICAL_PULLEY_SELFTEST_FAILED:
  229. case ERR_ELECTRICAL_SELECTOR_SELFTEST_FAILED:
  230. case ERR_ELECTRICAL_IDLER_SELFTEST_FAILED:
  231. case ERR_CONNECT_MMU_NOT_RESPONDING:
  232. case ERR_CONNECT_COMMUNICATION_ERROR:
  233. case ERR_SYSTEM_QUEUE_FULL:
  234. case ERR_SYSTEM_FW_RUNTIME_ERROR:
  235. switch (buttonSelectedOperation) {
  236. case ButtonOperations::RestartMMU: // "Restart MMU"
  237. return RestartMMU;
  238. default:
  239. break;
  240. }
  241. break;
  242. case ERR_SYSTEM_FW_UPDATE_NEEDED:
  243. switch (buttonSelectedOperation) {
  244. case ButtonOperations::DisableMMU: // "Disable"
  245. return DisableMMU;
  246. default:
  247. break;
  248. }
  249. break;
  250. case ERR_SYSTEM_FILAMENT_ALREADY_LOADED:
  251. switch (buttonSelectedOperation) {
  252. case ButtonOperations::Unload: // "Unload"
  253. return Left;
  254. case ButtonOperations::Continue: // "Proceed/Continue"
  255. return Right;
  256. default:
  257. break;
  258. }
  259. break;
  260. case ERR_SYSTEM_INVALID_TOOL:
  261. switch (buttonSelectedOperation) {
  262. case ButtonOperations::StopPrint: // "Stop print"
  263. return StopPrint;
  264. case ButtonOperations::RestartMMU: // "Restart MMU"
  265. return RestartMMU;
  266. default:
  267. break;
  268. }
  269. break;
  270. default:
  271. break;
  272. }
  273. return NoButton;
  274. }
  275. void SetButtonResponse(ButtonOperations rsp){
  276. buttonSelectedOperation = rsp;
  277. }
  278. } // namespace MMU2