mmu2_error_converter.cpp 13 KB

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