mmu2_error_converter.cpp 12 KB

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