mmu2_error_converter.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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::STALLED_PULLEY:
  45. case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED:
  46. return FindErrorIndex(ERR_MECHANICAL_PULLEY_CANNOT_MOVE);
  47. case (uint16_t)ErrorCode::HOMING_SELECTOR_FAILED:
  48. return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_HOME);
  49. case (uint16_t)ErrorCode::MOVE_SELECTOR_FAILED:
  50. return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_MOVE);
  51. case (uint16_t)ErrorCode::HOMING_IDLER_FAILED:
  52. return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_HOME);
  53. case (uint16_t)ErrorCode::MOVE_IDLER_FAILED:
  54. return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_MOVE);
  55. case (uint16_t)ErrorCode::MMU_NOT_RESPONDING:
  56. return FindErrorIndex(ERR_CONNECT_MMU_NOT_RESPONDING);
  57. case (uint16_t)ErrorCode::PROTOCOL_ERROR:
  58. return FindErrorIndex(ERR_CONNECT_COMMUNICATION_ERROR);
  59. case (uint16_t)ErrorCode::FILAMENT_ALREADY_LOADED:
  60. return FindErrorIndex(ERR_SYSTEM_FILAMENT_ALREADY_LOADED);
  61. case (uint16_t)ErrorCode::INVALID_TOOL:
  62. return FindErrorIndex(ERR_SYSTEM_INVALID_TOOL);
  63. case (uint16_t)ErrorCode::QUEUE_FULL:
  64. return FindErrorIndex(ERR_SYSTEM_QUEUE_FULL);
  65. case (uint16_t)ErrorCode::VERSION_MISMATCH:
  66. return FindErrorIndex(ERR_SYSTEM_FW_UPDATE_NEEDED);
  67. case (uint16_t)ErrorCode::INTERNAL:
  68. return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR);
  69. case (uint16_t)ErrorCode::FINDA_VS_EEPROM_DISREPANCY:
  70. return FindErrorIndex(ERR_SYSTEM_UNLOAD_MANUALLY);
  71. }
  72. // Electrical issues which can be detected somehow.
  73. // Need to be placed before TMC-related errors in order to process couples of error bits between single ones
  74. // and to keep the code size down.
  75. if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) {
  76. if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
  77. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_SELFTEST_FAILED);
  78. } else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) {
  79. if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
  80. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_SELFTEST_FAILED);
  81. } else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) {
  82. if ((ec & (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION)
  83. return FindErrorIndex(ERR_ELECTRICAL_IDLER_SELFTEST_FAILED);
  84. }
  85. // TMC-related errors - multiple of these can occur at once
  86. // - 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)
  87. // By carefully ordering the checks here we can prioritize the errors being reported to the user.
  88. if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) {
  89. if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH)
  90. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_ERROR);
  91. if (ec & (uint16_t)ErrorCode::TMC_RESET)
  92. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_RESET);
  93. if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)
  94. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_UNDERVOLTAGE_ERROR);
  95. if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND)
  96. return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_SHORTED);
  97. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN)
  98. return FindErrorIndex(ERR_TEMPERATURE_PULLEY_WARNING_TMC_TOO_HOT);
  99. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR)
  100. return FindErrorIndex(ERR_TEMPERATURE_PULLEY_TMC_OVERHEAT_ERROR);
  101. } else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) {
  102. if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH)
  103. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_ERROR);
  104. if (ec & (uint16_t)ErrorCode::TMC_RESET)
  105. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_RESET);
  106. if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)
  107. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_UNDERVOLTAGE_ERROR);
  108. if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND)
  109. return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_SHORTED);
  110. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN)
  111. return FindErrorIndex(ERR_TEMPERATURE_SELECTOR_WARNING_TMC_TOO_HOT);
  112. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR)
  113. return FindErrorIndex(ERR_TEMPERATURE_SELECTOR_TMC_OVERHEAT_ERROR);
  114. } else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) {
  115. if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH)
  116. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_ERROR);
  117. if (ec & (uint16_t)ErrorCode::TMC_RESET)
  118. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_RESET);
  119. if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)
  120. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_UNDERVOLTAGE_ERROR);
  121. if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND)
  122. return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_SHORTED);
  123. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN)
  124. return FindErrorIndex(ERR_TEMPERATURE_IDLER_WARNING_TMC_TOO_HOT);
  125. if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR)
  126. return FindErrorIndex(ERR_TEMPERATURE_IDLER_TMC_OVERHEAT_ERROR);
  127. }
  128. // if nothing got caught, return a generic runtime error
  129. return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR);
  130. }
  131. uint16_t PrusaErrorCode(uint8_t i){
  132. return pgm_read_word(errorCodes + i);
  133. }
  134. const char * PrusaErrorTitle(uint8_t i){
  135. return (const char *)pgm_read_ptr(errorTitles + i);
  136. }
  137. const char * PrusaErrorDesc(uint8_t i){
  138. return (const char *)pgm_read_ptr(errorDescs + i);
  139. }
  140. uint8_t PrusaErrorButtons(uint8_t i){
  141. return pgm_read_byte(errorButtons + i);
  142. }
  143. const char * PrusaErrorButtonTitle(uint8_t bi){
  144. // -1 represents the hidden NoOperation button which is not drawn in any way
  145. return (const char *)pgm_read_ptr(btnOperation + bi - 1);
  146. }
  147. const char * PrusaErrorButtonMore(){
  148. return _R(MSG_BTN_MORE);
  149. }
  150. struct ResetOnExit {
  151. ResetOnExit() = default;
  152. ~ResetOnExit(){
  153. buttonSelectedOperation = ButtonOperations::NoOperation;
  154. }
  155. };
  156. Buttons ButtonPressed(uint16_t ec) {
  157. if (buttonSelectedOperation == ButtonOperations::NoOperation) {
  158. return NoButton; // no button
  159. }
  160. ResetOnExit ros; // clear buttonSelectedOperation on exit from this call
  161. return ButtonAvailable(ec);
  162. }
  163. Buttons ButtonAvailable(uint16_t ec) {
  164. uint8_t ei = PrusaErrorCodeIndex(ec);
  165. // The list of responses which occur in mmu error dialogs
  166. // Return button index or perform some action on the MK3 by itself (like restart MMU)
  167. // Based on Prusa-Error-Codes errors_list.h
  168. // So far hardcoded, but shall be generated in the future
  169. switch ( PrusaErrorCode(ei) ) {
  170. case ERR_MECHANICAL_FINDA_DIDNT_TRIGGER:
  171. case ERR_MECHANICAL_FINDA_DIDNT_GO_OFF:
  172. switch (buttonSelectedOperation) {
  173. case ButtonOperations::Retry: // "Repeat action"
  174. return Middle;
  175. case ButtonOperations::Continue: // "Continue"
  176. return Right;
  177. default:
  178. break;
  179. }
  180. break;
  181. case ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER:
  182. case ERR_MECHANICAL_FSENSOR_DIDNT_GO_OFF:
  183. case ERR_MECHANICAL_FSENSOR_TOO_EARLY:
  184. case ERR_MECHANICAL_SELECTOR_CANNOT_HOME:
  185. case ERR_MECHANICAL_SELECTOR_CANNOT_MOVE:
  186. case ERR_MECHANICAL_IDLER_CANNOT_HOME:
  187. case ERR_MECHANICAL_IDLER_CANNOT_MOVE:
  188. case ERR_MECHANICAL_PULLEY_CANNOT_MOVE:
  189. case ERR_SYSTEM_UNLOAD_MANUALLY:
  190. switch (buttonSelectedOperation) {
  191. // may be allow move selector right and left in the future
  192. case ButtonOperations::Retry: // "Repeat action"
  193. return Middle;
  194. default:
  195. break;
  196. }
  197. break;
  198. case ERR_TEMPERATURE_PULLEY_WARNING_TMC_TOO_HOT:
  199. case ERR_TEMPERATURE_SELECTOR_WARNING_TMC_TOO_HOT:
  200. case ERR_TEMPERATURE_IDLER_WARNING_TMC_TOO_HOT:
  201. switch (buttonSelectedOperation) {
  202. case ButtonOperations::Continue: // "Continue"
  203. return Left;
  204. case ButtonOperations::RestartMMU: // "Restart MMU"
  205. return RestartMMU;
  206. default:
  207. break;
  208. }
  209. break;
  210. case ERR_TEMPERATURE_PULLEY_TMC_OVERHEAT_ERROR:
  211. case ERR_TEMPERATURE_SELECTOR_TMC_OVERHEAT_ERROR:
  212. case ERR_TEMPERATURE_IDLER_TMC_OVERHEAT_ERROR:
  213. case ERR_ELECTRICAL_PULLEY_TMC_DRIVER_ERROR:
  214. case ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_ERROR:
  215. case ERR_ELECTRICAL_IDLER_TMC_DRIVER_ERROR:
  216. case ERR_ELECTRICAL_PULLEY_TMC_DRIVER_RESET:
  217. case ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_RESET:
  218. case ERR_ELECTRICAL_IDLER_TMC_DRIVER_RESET:
  219. case ERR_ELECTRICAL_PULLEY_TMC_UNDERVOLTAGE_ERROR:
  220. case ERR_ELECTRICAL_SELECTOR_TMC_UNDERVOLTAGE_ERROR:
  221. case ERR_ELECTRICAL_IDLER_TMC_UNDERVOLTAGE_ERROR:
  222. case ERR_ELECTRICAL_PULLEY_TMC_DRIVER_SHORTED:
  223. case ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_SHORTED:
  224. case ERR_ELECTRICAL_IDLER_TMC_DRIVER_SHORTED:
  225. case ERR_CONNECT_MMU_NOT_RESPONDING:
  226. case ERR_CONNECT_COMMUNICATION_ERROR:
  227. case ERR_SYSTEM_QUEUE_FULL:
  228. case ERR_SYSTEM_FW_RUNTIME_ERROR:
  229. switch (buttonSelectedOperation) {
  230. case ButtonOperations::RestartMMU: // "Restart MMU"
  231. return RestartMMU;
  232. default:
  233. break;
  234. }
  235. break;
  236. case ERR_SYSTEM_FW_UPDATE_NEEDED:
  237. switch (buttonSelectedOperation) {
  238. case ButtonOperations::DisableMMU: // "Disable"
  239. return DisableMMU;
  240. default:
  241. break;
  242. }
  243. break;
  244. case ERR_SYSTEM_FILAMENT_ALREADY_LOADED:
  245. switch (buttonSelectedOperation) {
  246. case ButtonOperations::Unload: // "Unload"
  247. return Left;
  248. case ButtonOperations::Continue: // "Proceed/Continue"
  249. return Right;
  250. default:
  251. break;
  252. }
  253. break;
  254. case ERR_SYSTEM_INVALID_TOOL:
  255. switch (buttonSelectedOperation) {
  256. case ButtonOperations::StopPrint: // "Stop print"
  257. return StopPrint;
  258. case ButtonOperations::RestartMMU: // "Restart MMU"
  259. return RestartMMU;
  260. default:
  261. break;
  262. }
  263. break;
  264. default:
  265. break;
  266. }
  267. return NoButton;
  268. }
  269. void SetButtonResponse(ButtonOperations rsp){
  270. buttonSelectedOperation = rsp;
  271. }
  272. } // namespace MMU2