#include "mmu2_error_converter.h" #include "mmu2/error_codes.h" #include "mmu2/errors_list.h" #include "language.h" #include namespace MMU2 { // we don't have a constexpr find_if in C++17/STL yet template constexpr InputIt find_if_cx(InputIt first, InputIt last, UnaryPredicate p) { for (; first != last; ++first) { if (p(*first)) { return first; } } return last; } // Making a constexpr FindError should instruct the compiler to optimize the ConvertMMUErrorCode // in such a way that no searching will ever be done at runtime. // A call to FindError then compiles to a single instruction even on the AVR. static constexpr uint8_t FindErrorIndex(uint16_t pec) { constexpr uint16_t errorCodesSize = sizeof(errorCodes) / sizeof(errorCodes[0]); constexpr const auto *errorCodesEnd = errorCodes + errorCodesSize; const auto *i = find_if_cx(errorCodes, errorCodesEnd, [pec](uint16_t ed){ return ed == pec; }); return (i != errorCodesEnd) ? (i-errorCodes) : (errorCodesSize - 1); } // check that the searching algoritm works static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER) == 0); static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_GO_OFF) == 1); static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER) == 2); static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_GO_OFF) == 3); uint8_t PrusaErrorCodeIndex(uint16_t ec) { switch (ec) { case (uint16_t)ErrorCode::FINDA_DIDNT_SWITCH_ON: return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER); case (uint16_t)ErrorCode::FINDA_DIDNT_SWITCH_OFF: return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_GO_OFF); case (uint16_t)ErrorCode::FSENSOR_DIDNT_SWITCH_ON: return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER); case (uint16_t)ErrorCode::FSENSOR_DIDNT_SWITCH_OFF: return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_GO_OFF); case (uint16_t)ErrorCode::FSENSOR_TOO_EARLY: return FindErrorIndex(ERR_MECHANICAL_FSENSOR_TOO_EARLY); case (uint16_t)ErrorCode::STALLED_PULLEY: case (uint16_t)ErrorCode::MOVE_PULLEY_FAILED: return FindErrorIndex(ERR_MECHANICAL_PULLEY_CANNOT_MOVE); case (uint16_t)ErrorCode::HOMING_SELECTOR_FAILED: return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_HOME); case (uint16_t)ErrorCode::MOVE_SELECTOR_FAILED: return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_MOVE); case (uint16_t)ErrorCode::HOMING_IDLER_FAILED: return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_HOME); case (uint16_t)ErrorCode::MOVE_IDLER_FAILED: return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_MOVE); case (uint16_t)ErrorCode::MMU_NOT_RESPONDING: return FindErrorIndex(ERR_CONNECT_MMU_NOT_RESPONDING); case (uint16_t)ErrorCode::PROTOCOL_ERROR: return FindErrorIndex(ERR_CONNECT_COMMUNICATION_ERROR); case (uint16_t)ErrorCode::FILAMENT_ALREADY_LOADED: return FindErrorIndex(ERR_SYSTEM_FILAMENT_ALREADY_LOADED); case (uint16_t)ErrorCode::INVALID_TOOL: return FindErrorIndex(ERR_SYSTEM_INVALID_TOOL); case (uint16_t)ErrorCode::QUEUE_FULL: return FindErrorIndex(ERR_SYSTEM_QUEUE_FULL); case (uint16_t)ErrorCode::VERSION_MISMATCH: return FindErrorIndex(ERR_SYSTEM_FW_UPDATE_NEEDED); case (uint16_t)ErrorCode::INTERNAL: return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR); case (uint16_t)ErrorCode::FINDA_VS_EEPROM_DISREPANCY: return FindErrorIndex(ERR_SYSTEM_UNLOAD_MANUALLY); } // TMC-related errors - multiple of these can occur at once // - 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) // By carefully ordering the checks here we can prioritize the errors being reported to the user. if (ec & (uint16_t)ErrorCode::TMC_PULLEY_BIT) { if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH) return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_ERROR); if (ec & (uint16_t)ErrorCode::TMC_RESET) return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_RESET); if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP) return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_UNDERVOLTAGE_ERROR); if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND) return FindErrorIndex(ERR_ELECTRICAL_PULLEY_TMC_DRIVER_SHORTED); if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN) return FindErrorIndex(ERR_TEMPERATURE_PULLEY_WARNING_TMC_TOO_HOT); if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR) return FindErrorIndex(ERR_TEMPERATURE_PULLEY_TMC_OVERHEAT_ERROR); } else if (ec & (uint16_t)ErrorCode::TMC_SELECTOR_BIT) { if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH) return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_ERROR); if (ec & (uint16_t)ErrorCode::TMC_RESET) return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_RESET); if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP) return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_UNDERVOLTAGE_ERROR); if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND) return FindErrorIndex(ERR_ELECTRICAL_SELECTOR_TMC_DRIVER_SHORTED); if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN) return FindErrorIndex(ERR_TEMPERATURE_SELECTOR_WARNING_TMC_TOO_HOT); if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR) return FindErrorIndex(ERR_TEMPERATURE_SELECTOR_TMC_OVERHEAT_ERROR); } else if (ec & (uint16_t)ErrorCode::TMC_IDLER_BIT) { if (ec & (uint16_t)ErrorCode::TMC_IOIN_MISMATCH) return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_ERROR); if (ec & (uint16_t)ErrorCode::TMC_RESET) return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_RESET); if (ec & (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP) return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_UNDERVOLTAGE_ERROR); if (ec & (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND) return FindErrorIndex(ERR_ELECTRICAL_IDLER_TMC_DRIVER_SHORTED); if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN) return FindErrorIndex(ERR_TEMPERATURE_IDLER_WARNING_TMC_TOO_HOT); if (ec & (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR) return FindErrorIndex(ERR_TEMPERATURE_IDLER_TMC_OVERHEAT_ERROR); } // if nothing got caught, return a generic runtime error return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR); } uint16_t PrusaErrorCode(uint8_t i){ return pgm_read_word(errorCodes + i); } const char * const PrusaErrorTitle(uint8_t i){ return (const char * const)pgm_read_ptr(errorTitles + i); } const char * const PrusaErrorDesc(uint8_t i){ return (const char * const)pgm_read_ptr(errorDescs + i); } uint8_t PrusaErrorButtons(uint8_t i){ return pgm_read_byte(errorButtons + i); } const char * const PrusaErrorButtonTitle(uint8_t bi){ // -1 represents the hidden NoOperation button which is not drawn in any way return (const char * const)pgm_read_ptr(btnOperation + bi - 1); } const char * const PrusaErrorButtonMore(){ return btnMore; } } // namespace MMU2