mmu2_error_converter.cpp 14 KB

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